OpenBCI-运动想象BCI:从信号采集到动作识别
文章目录
- OpenBCI-运动想象BCI:从信号采集到动作识别
-
- 引言
- 运动想象基础
-
- 什么是运动想象?
- 运动想象的神经机制
- 常见的运动想象任务
- 事件相关去同步/同步(ERD/ERS)
- 数据采集与预处理
-
- 电极放置
- 数据采集代码
- 预处理流程
- 特征提取
-
- 常用特征提取方法
- 共同空间模式(CSP)
- 频域特征提取
- 分类算法
-
- 线性判别分析(LDA)
- 完整训练流程
- 实时运动想象BCI系统
-
- 系统架构
- 实时处理代码
- 实验设计
-
- 典型实验范式
- 实验流程代码
- 性能评估
-
- 评估指标
- 离线分析
- 常见问题与优化
-
- 问题1:信号质量差
- 问题2:分类准确率低
- 问题3:过拟合
- 总结
- 思考与练习
引言
运动想象(Motor Imagery, MI)是脑机接口领域中最经典的范式之一。通过运动想象BCI,用户可以通过想象肢体运动来控制外部设备,这对于运动障碍患者的康复具有重要意义。
本文将详细介绍运动想象BCI的原理、数据采集方法、特征提取和分类算法,并提供完整的代码实现。
运动想象基础
什么是运动想象?
运动想象是指在没有实际肢体运动的情况下,在大脑中模拟运动过程。当我们想象运动时,大脑运动皮层会产生特定的脑电信号模式。
运动想象的神经机制
#mermaid-svg-otqBbYDh1MDpnKHK{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-otqBbYDh1MDpnKHK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-otqBbYDh1MDpnKHK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-otqBbYDh1MDpnKHK .error-icon{fill:#552222;}#mermaid-svg-otqBbYDh1MDpnKHK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-otqBbYDh1MDpnKHK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-otqBbYDh1MDpnKHK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-otqBbYDh1MDpnKHK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-otqBbYDh1MDpnKHK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-otqBbYDh1MDpnKHK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-otqBbYDh1MDpnKHK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-otqBbYDh1MDpnKHK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-otqBbYDh1MDpnKHK .marker.cross{stroke:#333333;}#mermaid-svg-otqBbYDh1MDpnKHK svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-otqBbYDh1MDpnKHK p{margin:0;}#mermaid-svg-otqBbYDh1MDpnKHK .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-otqBbYDh1MDpnKHK .cluster-label text{fill:#333;}#mermaid-svg-otqBbYDh1MDpnKHK .cluster-label span{color:#333;}#mermaid-svg-otqBbYDh1MDpnKHK .cluster-label span p{background-color:transparent;}#mermaid-svg-otqBbYDh1MDpnKHK .label text,#mermaid-svg-otqBbYDh1MDpnKHK span{fill:#333;color:#333;}#mermaid-svg-otqBbYDh1MDpnKHK .node rect,#mermaid-svg-otqBbYDh1MDpnKHK .node circle,#mermaid-svg-otqBbYDh1MDpnKHK .node ellipse,#mermaid-svg-otqBbYDh1MDpnKHK .node polygon,#mermaid-svg-otqBbYDh1MDpnKHK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-otqBbYDh1MDpnKHK .rough-node .label text,#mermaid-svg-otqBbYDh1MDpnKHK .node .label text,#mermaid-svg-otqBbYDh1MDpnKHK .image-shape .label,#mermaid-svg-otqBbYDh1MDpnKHK .icon-shape .label{text-anchor:middle;}#mermaid-svg-otqBbYDh1MDpnKHK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-otqBbYDh1MDpnKHK .rough-node .label,#mermaid-svg-otqBbYDh1MDpnKHK .node .label,#mermaid-svg-otqBbYDh1MDpnKHK .image-shape .label,#mermaid-svg-otqBbYDh1MDpnKHK .icon-shape .label{text-align:center;}#mermaid-svg-otqBbYDh1MDpnKHK .node.clickable{cursor:pointer;}#mermaid-svg-otqBbYDh1MDpnKHK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-otqBbYDh1MDpnKHK .arrowheadPath{fill:#333333;}#mermaid-svg-otqBbYDh1MDpnKHK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-otqBbYDh1MDpnKHK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-otqBbYDh1MDpnKHK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-otqBbYDh1MDpnKHK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-otqBbYDh1MDpnKHK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-otqBbYDh1MDpnKHK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-otqBbYDh1MDpnKHK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-otqBbYDh1MDpnKHK .cluster text{fill:#333;}#mermaid-svg-otqBbYDh1MDpnKHK .cluster span{color:#333;}#mermaid-svg-otqBbYDh1MDpnKHK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-otqBbYDh1MDpnKHK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-otqBbYDh1MDpnKHK rect.text{fill:none;stroke-width:0;}#mermaid-svg-otqBbYDh1MDpnKHK .icon-shape,#mermaid-svg-otqBbYDh1MDpnKHK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-otqBbYDh1MDpnKHK .icon-shape p,#mermaid-svg-otqBbYDh1MDpnKHK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-otqBbYDh1MDpnKHK .icon-shape .label rect,#mermaid-svg-otqBbYDh1MDpnKHK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-otqBbYDh1MDpnKHK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-otqBbYDh1MDpnKHK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-otqBbYDh1MDpnKHK :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
用户想象运动
运动皮层激活
产生特定EEG模式
传感器采集
信号处理
特征提取
分类识别
控制指令
外部设备
常见的运动想象任务
| 手部运动 | 想象握拳、抬手 | 初级运动皮层(M1) |
| 脚部运动 | 想象抬脚、踏步 | 下肢运动皮层 |
| 舌头运动 | 想象伸舌头 | 面部运动皮层 |
| 手部方向 | 想象左手/右手运动 | 对侧运动皮层 |
事件相关去同步/同步(ERD/ERS)
运动想象会引起特定频段脑电信号的变化:
#mermaid-svg-HHQwHXWpR4NvSsWW{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HHQwHXWpR4NvSsWW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HHQwHXWpR4NvSsWW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HHQwHXWpR4NvSsWW .error-icon{fill:#552222;}#mermaid-svg-HHQwHXWpR4NvSsWW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HHQwHXWpR4NvSsWW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HHQwHXWpR4NvSsWW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HHQwHXWpR4NvSsWW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HHQwHXWpR4NvSsWW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HHQwHXWpR4NvSsWW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HHQwHXWpR4NvSsWW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HHQwHXWpR4NvSsWW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HHQwHXWpR4NvSsWW .marker.cross{stroke:#333333;}#mermaid-svg-HHQwHXWpR4NvSsWW svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HHQwHXWpR4NvSsWW p{margin:0;}#mermaid-svg-HHQwHXWpR4NvSsWW .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-HHQwHXWpR4NvSsWW .cluster-label text{fill:#333;}#mermaid-svg-HHQwHXWpR4NvSsWW .cluster-label span{color:#333;}#mermaid-svg-HHQwHXWpR4NvSsWW .cluster-label span p{background-color:transparent;}#mermaid-svg-HHQwHXWpR4NvSsWW .label text,#mermaid-svg-HHQwHXWpR4NvSsWW span{fill:#333;color:#333;}#mermaid-svg-HHQwHXWpR4NvSsWW .node rect,#mermaid-svg-HHQwHXWpR4NvSsWW .node circle,#mermaid-svg-HHQwHXWpR4NvSsWW .node ellipse,#mermaid-svg-HHQwHXWpR4NvSsWW .node polygon,#mermaid-svg-HHQwHXWpR4NvSsWW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HHQwHXWpR4NvSsWW .rough-node .label text,#mermaid-svg-HHQwHXWpR4NvSsWW .node .label text,#mermaid-svg-HHQwHXWpR4NvSsWW .image-shape .label,#mermaid-svg-HHQwHXWpR4NvSsWW .icon-shape .label{text-anchor:middle;}#mermaid-svg-HHQwHXWpR4NvSsWW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HHQwHXWpR4NvSsWW .rough-node .label,#mermaid-svg-HHQwHXWpR4NvSsWW .node .label,#mermaid-svg-HHQwHXWpR4NvSsWW .image-shape .label,#mermaid-svg-HHQwHXWpR4NvSsWW .icon-shape .label{text-align:center;}#mermaid-svg-HHQwHXWpR4NvSsWW .node.clickable{cursor:pointer;}#mermaid-svg-HHQwHXWpR4NvSsWW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HHQwHXWpR4NvSsWW .arrowheadPath{fill:#333333;}#mermaid-svg-HHQwHXWpR4NvSsWW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HHQwHXWpR4NvSsWW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HHQwHXWpR4NvSsWW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HHQwHXWpR4NvSsWW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HHQwHXWpR4NvSsWW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HHQwHXWpR4NvSsWW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HHQwHXWpR4NvSsWW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HHQwHXWpR4NvSsWW .cluster text{fill:#333;}#mermaid-svg-HHQwHXWpR4NvSsWW .cluster span{color:#333;}#mermaid-svg-HHQwHXWpR4NvSsWW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HHQwHXWpR4NvSsWW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HHQwHXWpR4NvSsWW rect.text{fill:none;stroke-width:0;}#mermaid-svg-HHQwHXWpR4NvSsWW .icon-shape,#mermaid-svg-HHQwHXWpR4NvSsWW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HHQwHXWpR4NvSsWW .icon-shape p,#mermaid-svg-HHQwHXWpR4NvSsWW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HHQwHXWpR4NvSsWW .icon-shape .label rect,#mermaid-svg-HHQwHXWpR4NvSsWW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HHQwHXWpR4NvSsWW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HHQwHXWpR4NvSsWW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HHQwHXWpR4NvSsWW :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
运动想象期间
ERD:能量下降
Mu节律减弱
ERS:能量上升
Beta节律增强
运动想象前
Mu节律活跃
8-12Hz
数据采集与预处理
电极放置
运动想象BCI通常使用国际10-20系统放置电极:
#mermaid-svg-vlsndGgPIDE6VFg1{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vlsndGgPIDE6VFg1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vlsndGgPIDE6VFg1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vlsndGgPIDE6VFg1 .error-icon{fill:#552222;}#mermaid-svg-vlsndGgPIDE6VFg1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vlsndGgPIDE6VFg1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vlsndGgPIDE6VFg1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vlsndGgPIDE6VFg1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vlsndGgPIDE6VFg1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vlsndGgPIDE6VFg1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vlsndGgPIDE6VFg1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vlsndGgPIDE6VFg1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vlsndGgPIDE6VFg1 .marker.cross{stroke:#333333;}#mermaid-svg-vlsndGgPIDE6VFg1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vlsndGgPIDE6VFg1 p{margin:0;}#mermaid-svg-vlsndGgPIDE6VFg1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-vlsndGgPIDE6VFg1 .cluster-label text{fill:#333;}#mermaid-svg-vlsndGgPIDE6VFg1 .cluster-label span{color:#333;}#mermaid-svg-vlsndGgPIDE6VFg1 .cluster-label span p{background-color:transparent;}#mermaid-svg-vlsndGgPIDE6VFg1 .label text,#mermaid-svg-vlsndGgPIDE6VFg1 span{fill:#333;color:#333;}#mermaid-svg-vlsndGgPIDE6VFg1 .node rect,#mermaid-svg-vlsndGgPIDE6VFg1 .node circle,#mermaid-svg-vlsndGgPIDE6VFg1 .node ellipse,#mermaid-svg-vlsndGgPIDE6VFg1 .node polygon,#mermaid-svg-vlsndGgPIDE6VFg1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vlsndGgPIDE6VFg1 .rough-node .label text,#mermaid-svg-vlsndGgPIDE6VFg1 .node .label text,#mermaid-svg-vlsndGgPIDE6VFg1 .image-shape .label,#mermaid-svg-vlsndGgPIDE6VFg1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-vlsndGgPIDE6VFg1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vlsndGgPIDE6VFg1 .rough-node .label,#mermaid-svg-vlsndGgPIDE6VFg1 .node .label,#mermaid-svg-vlsndGgPIDE6VFg1 .image-shape .label,#mermaid-svg-vlsndGgPIDE6VFg1 .icon-shape .label{text-align:center;}#mermaid-svg-vlsndGgPIDE6VFg1 .node.clickable{cursor:pointer;}#mermaid-svg-vlsndGgPIDE6VFg1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vlsndGgPIDE6VFg1 .arrowheadPath{fill:#333333;}#mermaid-svg-vlsndGgPIDE6VFg1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vlsndGgPIDE6VFg1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vlsndGgPIDE6VFg1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vlsndGgPIDE6VFg1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vlsndGgPIDE6VFg1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vlsndGgPIDE6VFg1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vlsndGgPIDE6VFg1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vlsndGgPIDE6VFg1 .cluster text{fill:#333;}#mermaid-svg-vlsndGgPIDE6VFg1 .cluster span{color:#333;}#mermaid-svg-vlsndGgPIDE6VFg1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vlsndGgPIDE6VFg1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vlsndGgPIDE6VFg1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-vlsndGgPIDE6VFg1 .icon-shape,#mermaid-svg-vlsndGgPIDE6VFg1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vlsndGgPIDE6VFg1 .icon-shape p,#mermaid-svg-vlsndGgPIDE6VFg1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vlsndGgPIDE6VFg1 .icon-shape .label rect,#mermaid-svg-vlsndGgPIDE6VFg1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vlsndGgPIDE6VFg1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vlsndGgPIDE6VFg1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vlsndGgPIDE6VFg1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
大脑顶部视图
C3
左运动皮层
C4
右运动皮层
Cz
中央
数据采集代码
from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds
import numpy as np
class MIDataCollector:
def __init__(self):
self.params = BrainFlowInputParams()
self.board = None
def connect(self, board_id=BoardIds.CYTON_BOARD.value):
"""连接设备"""
try:
self.board = BoardShim(board_id, self.params)
self.board.prepare_session()
self.board.start_stream()
return True
except Exception as e:
print(f"连接失败: {e}")
return False
def collect_trial(self, duration=3):
"""采集一次试次的数据"""
samples_per_second = 250
total_samples = duration * samples_per_second
# 等待数据积累
import time
time.sleep(duration)
# 获取数据
data = self.board.get_current_board_data(total_samples)
# 提取EEG通道(C3, C4, Cz)
# 对于Cyton板,通道1-8对应EEG
eeg_channels = [1, 2, 3] # C3, C4, Cz
eeg_data = data[eeg_channels, :].T
return eeg_data
def disconnect(self):
"""断开连接"""
if self.board:
self.board.stop_stream()
self.board.release_session()
预处理流程
from scipy.signal import butter, lfilter
class MIPreprocessor:
def __init__(self, fs=250):
self.fs = fs
def butter_bandpass(self, lowcut, highcut, order=4):
"""设计巴特沃斯带通滤波器"""
nyq = 0.5 * self.fs
low = lowcut / nyq
high = highcut / nyq
b, a = butter(order, [low, high], btype='band')
return b, a
def filter(self, data, lowcut=8, highcut=30):
"""滤波(保留mu和beta频段)"""
b, a = self.butter_bandpass(lowcut, highcut)
filtered = lfilter(b, a, data, axis=0)
return filtered
def normalize(self, data):
"""数据归一化"""
mean = np.mean(data, axis=0)
std = np.std(data, axis=0)
normalized = (data – mean) / (std + 1e-10)
return normalized
def process(self, data):
"""完整预处理流程"""
data = self.filter(data)
data = self.normalize(data)
return data
特征提取
常用特征提取方法
| 时域特征 | 均值、方差、峰值 | 简单但效果有限 |
| 频域特征 | PSD、频段功率 | 最常用 |
| 时频特征 | STFT、小波变换 | 捕捉时间-频率变化 |
| 空间特征 | 共同空间模式(CSP) | 专门用于MI |
共同空间模式(CSP)
CSP是运动想象BCI中最经典的特征提取方法:
class CSP:
def __init__(self, n_components=4):
self.n_components = n_components
self.filters = None
def fit(self, X_left, X_right):
"""训练CSP滤波器"""
# 计算协方差矩阵
cov_left = np.mean([np.dot(x.T, x) for x in X_left], axis=0)
cov_right = np.mean([np.dot(x.T, x) for x in X_right], axis=0)
# 白化
cov_total = cov_left + cov_right
eigvals, eigvecs = np.linalg.eigh(cov_total)
# 排序
idx = np.argsort(eigvals)[::–1]
eigvals = eigvals[idx]
eigvecs = eigvecs[:, idx]
# 白化矩阵
whitening = np.dot(np.diag(1/np.sqrt(eigvals)), eigvecs.T)
# 白化后的协方差
cov_left_white = np.dot(np.dot(whitening, cov_left), whitening.T)
# 求解特征值
eigvals_csp, eigvecs_csp = np.linalg.eigh(cov_left_white)
idx = np.argsort(eigvals_csp)[::–1]
self.filters = eigvecs_csp[:, idx]
def transform(self, X):
"""应用CSP滤波"""
if self.filters is None:
raise Exception("CSP尚未训练")
# 选择前n_components个滤波器
selected_filters = self.filters[:, :self.n_components]
# 应用滤波
features = []
for trial in X:
z = np.dot(selected_filters.T, trial.T)
# 计算对数方差特征
var_features = np.log(np.var(z, axis=1))
features.append(var_features)
return np.array(features)
频域特征提取
from scipy.signal import welch
class FrequencyFeatures:
def __init__(self, fs=250):
self.fs = fs
self.bands = {
'mu': (8, 12),
'beta': (13, 30)
}
def extract_psd_features(self, data):
"""提取PSD特征"""
features = []
for channel in data.T:
freqs, psd = welch(channel, fs=self.fs, nperseg=256)
for band_name, (low, high) in self.bands.items():
mask = (freqs >= low) & (freqs <= high)
band_power = np.mean(psd[mask])
features.append(band_power)
# 添加频段比率
mu_power = features[0::2]
beta_power = features[1::2]
features.append(np.mean(beta_power) / (np.mean(mu_power) + 1e-10))
return np.array(features)
分类算法
线性判别分析(LDA)
LDA是运动想象BCI中最常用的分类器:
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.model_selection import cross_val_score
class MIClassifier:
def __init__(self):
self.lda = LinearDiscriminantAnalysis()
self.csp = CSP(n_components=4)
def train(self, X_left, X_right):
"""训练模型"""
# 准备数据
X = np.vstack([X_left, X_right])
y = np.array([0]*len(X_left) + [1]*len(X_right))
# 应用CSP
X_features = self.csp.transform(X)
# 训练LDA
self.lda.fit(X_features, y)
# 交叉验证
scores = cross_val_score(self.lda, X_features, y, cv=5)
print(f"交叉验证准确率: {np.mean(scores):.2f} ± {np.std(scores):.2f}")
def predict(self, data):
"""预测类别"""
features = self.csp.transform([data])
prediction = self.lda.predict(features)
probability = self.lda.predict_proba(features)
return prediction[0], probability[0]
完整训练流程
# 模拟训练数据
np.random.seed(42)
n_trials = 100
n_samples = 750 # 3秒 * 250Hz
n_channels = 3
# 左手想象数据
left_trials = []
for _ in range(n_trials):
data = np.random.normal(loc=0, scale=1, size=(n_samples, n_channels))
# 添加mu节律减弱特征
t = np.arange(n_samples) / 250
data[:, 0] -= np.sin(2 * np.pi * 10 * t) * 0.5 # C3通道mu减弱
left_trials.append(data)
# 右手想象数据
right_trials = []
for _ in range(n_trials):
data = np.random.normal(loc=0, scale=1, size=(n_samples, n_channels))
# 添加mu节律减弱特征
data[:, 1] -= np.sin(2 * np.pi * 10 * t) * 0.5 # C4通道mu减弱
right_trials.append(data)
# 预处理
preprocessor = MIPreprocessor()
left_trials = [preprocessor.process(trial) for trial in left_trials]
right_trials = [preprocessor.process(trial) for trial in right_trials]
# 训练分类器
classifier = MIClassifier()
classifier.train(left_trials, right_trials)
实时运动想象BCI系统
系统架构
#mermaid-svg-U3V3oBd5SZ01sBqX{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-U3V3oBd5SZ01sBqX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-U3V3oBd5SZ01sBqX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-U3V3oBd5SZ01sBqX .error-icon{fill:#552222;}#mermaid-svg-U3V3oBd5SZ01sBqX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-U3V3oBd5SZ01sBqX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-U3V3oBd5SZ01sBqX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-U3V3oBd5SZ01sBqX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-U3V3oBd5SZ01sBqX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-U3V3oBd5SZ01sBqX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-U3V3oBd5SZ01sBqX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-U3V3oBd5SZ01sBqX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-U3V3oBd5SZ01sBqX .marker.cross{stroke:#333333;}#mermaid-svg-U3V3oBd5SZ01sBqX svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-U3V3oBd5SZ01sBqX p{margin:0;}#mermaid-svg-U3V3oBd5SZ01sBqX .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-U3V3oBd5SZ01sBqX .cluster-label text{fill:#333;}#mermaid-svg-U3V3oBd5SZ01sBqX .cluster-label span{color:#333;}#mermaid-svg-U3V3oBd5SZ01sBqX .cluster-label span p{background-color:transparent;}#mermaid-svg-U3V3oBd5SZ01sBqX .label text,#mermaid-svg-U3V3oBd5SZ01sBqX span{fill:#333;color:#333;}#mermaid-svg-U3V3oBd5SZ01sBqX .node rect,#mermaid-svg-U3V3oBd5SZ01sBqX .node circle,#mermaid-svg-U3V3oBd5SZ01sBqX .node ellipse,#mermaid-svg-U3V3oBd5SZ01sBqX .node polygon,#mermaid-svg-U3V3oBd5SZ01sBqX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-U3V3oBd5SZ01sBqX .rough-node .label text,#mermaid-svg-U3V3oBd5SZ01sBqX .node .label text,#mermaid-svg-U3V3oBd5SZ01sBqX .image-shape .label,#mermaid-svg-U3V3oBd5SZ01sBqX .icon-shape .label{text-anchor:middle;}#mermaid-svg-U3V3oBd5SZ01sBqX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-U3V3oBd5SZ01sBqX .rough-node .label,#mermaid-svg-U3V3oBd5SZ01sBqX .node .label,#mermaid-svg-U3V3oBd5SZ01sBqX .image-shape .label,#mermaid-svg-U3V3oBd5SZ01sBqX .icon-shape .label{text-align:center;}#mermaid-svg-U3V3oBd5SZ01sBqX .node.clickable{cursor:pointer;}#mermaid-svg-U3V3oBd5SZ01sBqX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-U3V3oBd5SZ01sBqX .arrowheadPath{fill:#333333;}#mermaid-svg-U3V3oBd5SZ01sBqX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-U3V3oBd5SZ01sBqX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-U3V3oBd5SZ01sBqX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U3V3oBd5SZ01sBqX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-U3V3oBd5SZ01sBqX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U3V3oBd5SZ01sBqX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-U3V3oBd5SZ01sBqX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-U3V3oBd5SZ01sBqX .cluster text{fill:#333;}#mermaid-svg-U3V3oBd5SZ01sBqX .cluster span{color:#333;}#mermaid-svg-U3V3oBd5SZ01sBqX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-U3V3oBd5SZ01sBqX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-U3V3oBd5SZ01sBqX rect.text{fill:none;stroke-width:0;}#mermaid-svg-U3V3oBd5SZ01sBqX .icon-shape,#mermaid-svg-U3V3oBd5SZ01sBqX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U3V3oBd5SZ01sBqX .icon-shape p,#mermaid-svg-U3V3oBd5SZ01sBqX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-U3V3oBd5SZ01sBqX .icon-shape .label rect,#mermaid-svg-U3V3oBd5SZ01sBqX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U3V3oBd5SZ01sBqX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-U3V3oBd5SZ01sBqX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-U3V3oBd5SZ01sBqX :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
否
是
低
高
OpenBCI设备
数据采集
缓冲区
缓冲区满?
预处理
特征提取
分类预测
输出控制指令
外部设备
置信度判断
丢弃
实时处理代码
class RealTimeMIBCI:
def __init__(self):
self.collector = MIDataCollector()
self.preprocessor = MIPreprocessor()
self.classifier = MIClassifier()
self.buffer = []
self.buffer_size = 750 # 3秒数据
self.confidence_threshold = 0.8
def load_model(self, model_path):
"""加载训练好的模型"""
import joblib
self.classifier = joblib.load(model_path)
def run(self):
"""运行实时系统"""
if not self.collector.connect():
print("无法连接设备")
return
print("实时运动想象BCI系统启动")
print("想象左手或右手运动…")
try:
while True:
# 获取最新数据
data = self.collector.board.get_current_board_data(100)
eeg_data = data[1:4, :].T # C3, C4, Cz
# 更新缓冲区
self.buffer.extend(eeg_data.tolist())
if len(self.buffer) > self.buffer_size:
self.buffer = self.buffer[–self.buffer_size:]
# 当缓冲区满时进行预测
if len(self.buffer) == self.buffer_size:
# 预处理
processed_data = self.preprocessor.process(np.array(self.buffer))
# 预测
prediction, prob = self.classifier.predict(processed_data)
confidence = prob[prediction]
# 输出结果
if confidence > self.confidence_threshold:
action = "左手" if prediction == 0 else "右手"
print(f"预测: {action}, 置信度: {confidence:.2f}")
self.execute_action(action)
time.sleep(0.01)
except KeyboardInterrupt:
print("\\n系统停止")
finally:
self.collector.disconnect()
def execute_action(self, action):
"""执行动作"""
print(f"执行动作: {action}")
# 这里可以添加控制外部设备的代码
实验设计
典型实验范式
用户
计算机
用户
计算机
#mermaid-svg-2XmGGIcOZqy5ZEvP{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2XmGGIcOZqy5ZEvP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2XmGGIcOZqy5ZEvP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2XmGGIcOZqy5ZEvP .error-icon{fill:#552222;}#mermaid-svg-2XmGGIcOZqy5ZEvP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2XmGGIcOZqy5ZEvP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2XmGGIcOZqy5ZEvP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2XmGGIcOZqy5ZEvP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2XmGGIcOZqy5ZEvP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2XmGGIcOZqy5ZEvP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2XmGGIcOZqy5ZEvP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2XmGGIcOZqy5ZEvP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2XmGGIcOZqy5ZEvP .marker.cross{stroke:#333333;}#mermaid-svg-2XmGGIcOZqy5ZEvP svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2XmGGIcOZqy5ZEvP p{margin:0;}#mermaid-svg-2XmGGIcOZqy5ZEvP .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2XmGGIcOZqy5ZEvP text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-2XmGGIcOZqy5ZEvP .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-2XmGGIcOZqy5ZEvP .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-2XmGGIcOZqy5ZEvP .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-2XmGGIcOZqy5ZEvP .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-2XmGGIcOZqy5ZEvP #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-2XmGGIcOZqy5ZEvP .sequenceNumber{fill:white;}#mermaid-svg-2XmGGIcOZqy5ZEvP #sequencenumber{fill:#333;}#mermaid-svg-2XmGGIcOZqy5ZEvP #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-2XmGGIcOZqy5ZEvP .messageText{fill:#333;stroke:none;}#mermaid-svg-2XmGGIcOZqy5ZEvP .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2XmGGIcOZqy5ZEvP .labelText,#mermaid-svg-2XmGGIcOZqy5ZEvP .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-2XmGGIcOZqy5ZEvP .loopText,#mermaid-svg-2XmGGIcOZqy5ZEvP .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-2XmGGIcOZqy5ZEvP .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-2XmGGIcOZqy5ZEvP .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-2XmGGIcOZqy5ZEvP .noteText,#mermaid-svg-2XmGGIcOZqy5ZEvP .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-2XmGGIcOZqy5ZEvP .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2XmGGIcOZqy5ZEvP .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2XmGGIcOZqy5ZEvP .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2XmGGIcOZqy5ZEvP .actorPopupMenu{position:absolute;}#mermaid-svg-2XmGGIcOZqy5ZEvP .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-2XmGGIcOZqy5ZEvP .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2XmGGIcOZqy5ZEvP .actor-man circle,#mermaid-svg-2XmGGIcOZqy5ZEvP line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-2XmGGIcOZqy5ZEvP :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
休息1秒
想象3秒
休息2秒
想象3秒
显示"准备"
显示"想象左手"
显示"休息"
显示"想象右手"
实验流程代码
class MIExperiment:
def __init__(self):
self.collector = MIDataCollector()
self.trials_per_class = 50
self.data = {'left': [], 'right': []}
def run(self):
"""运行实验"""
if not self.collector.connect():
return
print("运动想象实验开始")
print("按照屏幕提示进行想象")
for trial in range(self.trials_per_class):
# 左手想象
input("按回车开始左手想象…")
print("想象左手运动…")
data = self.collector.collect_trial(duration=3)
self.data['left'].append(data)
print("完成\\n")
# 右手想象
input("按回车开始右手想象…")
print("想象右手运动…")
data = self.collector.collect_trial(duration=3)
self.data['right'].append(data)
print("完成\\n")
self.collector.disconnect()
print("实验完成")
# 保存数据
np.savez('mi_data.npz', left=self.data['left'], right=self.data['right'])
print("数据已保存")
性能评估
评估指标
from sklearn.metrics import confusion_matrix, accuracy_score
def evaluate_model(classifier, X_test, y_test):
"""评估模型性能"""
y_pred = classifier.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
conf_matrix = confusion_matrix(y_test, y_pred)
print(f"准确率: {accuracy:.2f}")
print("混淆矩阵:")
print(conf_matrix)
# 计算kappa系数
from sklearn.metrics import cohen_kappa_score
kappa = cohen_kappa_score(y_test, y_pred)
print(f"Kappa系数: {kappa:.2f}")
return accuracy, conf_matrix, kappa
离线分析
def offline_analysis(data_path):
"""离线数据分析"""
data = np.load(data_path)
X_left = data['left']
X_right = data['right']
# 预处理
preprocessor = MIPreprocessor()
X_left = [preprocessor.process(x) for x in X_left]
X_right = [preprocessor.process(x) for x in X_right]
# 划分训练集和测试集
from sklearn.model_selection import train_test_split
X_train_l, X_test_l = train_test_split(X_left, test_size=0.2)
X_train_r, X_test_r = train_test_split(X_right, test_size=0.2)
# 训练模型
classifier = MIClassifier()
classifier.train(X_train_l, X_train_r)
# 测试
X_test = X_test_l + X_test_r
y_test = [0]*len(X_test_l) + [1]*len(X_test_r)
X_features = classifier.csp.transform(X_test)
evaluate_model(classifier.lda, X_features, y_test)
常见问题与优化
问题1:信号质量差
解决方案:
- 确保电极接触良好
- 使用导电膏
- 保持环境安静
问题2:分类准确率低
解决方案:
- 增加训练数据量
- 优化特征提取方法
- 尝试不同的分类器
问题3:过拟合
解决方案:
- 使用交叉验证
- 增加正则化
- 减少特征维度
总结
通过本文,你学会了:
思考与练习
系列文章导航:
- 第1篇:什么是脑机接口(BCI)?OpenBCI入门指南
- 第2篇:OpenBCI硬件选型:Cyton vs Ganglion对比分析
- 第3篇:搭建你的第一个脑电采集系统
- 第4篇:OpenBCI GUI使用指南:从安装到数据采集
- 第5篇:脑电信号基础:EEG波形解读与频段分析
- 第6篇:BrainFlow SDK完全指南:统一API实现多设备兼容
- 第7篇:Python与OpenBCI:实时脑电信号采集实战
- 第8篇:信号预处理:滤波、去噪与伪迹去除
- 第9篇:特征提取技术:频域分析与时频分析
- 第10篇:机器学习入门:从脑电信号到模式识别
- 第11篇:实战一:脑波控制LED灯(基础)
- 第12篇:实战二:脑波控制小游戏开发
- 第13篇:实战三:注意力监测系统
- 第14篇:实战四:情绪识别与反馈系统
- 第15篇:实战五:脑电数据可视化仪表板
- 第16篇:运动想象BCI:从信号采集到动作识别(当前)
- 第17篇:P300拼写器:用大脑打字
声明:本文仅供学习交流,文中涉及的硬件设备和软件工具请从官方渠道获取。脑机接口技术涉及生物医学领域,实际应用请遵循相关法律法规和伦理规范。






