BrainFlow 库的使用方法
官网:BrainFlow
API :User API — BrainFlow documentation
编译安装
脚本 build-brainflow-5.22.2.bat 如下,运行流程是“设置 VS 环境-> 解压-> 编译安装-> 修改绝对路径”。
需要把 brainflow-5.22.2.tar.gz 与脚本放在同一目录,且按需替换 vs_dir、build_generator 和 output_dir 的值。
@echo off
chcp 65001
for %%i in ("%~dp0.") do set "script_dir=%%~fi"
set "vs_dir=K:\jc39\Program\Files\Microsoft Visual Studio\2022\Community"
if not defined VisualStudioVersion (
call "%vs_dir%\VC\Auxiliary\Build\vcvarsall.bat" x64
)
set "tar_file=%script_dir%\brainflow-5.22.2.tar.gz"
set "code_dir=%script_dir%\brainflow-5.22.2"
rmdir /s /q "%code_dir%"
pushd "%script_dir%"
cmake -E tar zxf "%tar_file%"
popd
set "build_dir=%code_dir%\build"
set "build_jobs=%NUMBER_OF_PROCESSORS%"
set "build_generator=Visual Studio 17 2022"
set "output_dir=J:\jc39\project\csis\mest-cpp\bci\beqind\pkg\brainflow"
rmdir /s /q "%build_dir%"
mkdir "%build_dir%"
mkdir "%output_dir%"
cmake -S "%code_dir%" -B "%build_dir%" ^
-G "%build_generator%" -A x64 ^
-DCMAKE_BUILD_TYPE="Release" ^
-DCMAKE_INSTALL_PREFIX="%output_dir%"
cmake --build "%build_dir%" --config "Release" --parallel "%build_jobs%"
cmake --build "%build_dir%" --config "Release" --target install
powershell -Command ^
"$p=$env:output_dir.Replace('\','/'); $f=Join-Path $env:output_dir 'lib\cmake\brainflow\brainflowConfig.cmake'; (Get-Content $f -Raw).Replace($p,'$'+'{PACKAGE_PREFIX_DIR}') | Set-Content $f"
echo [+] Done
配置项目
项目结构
beqind/
├── build
│ └── win32-x86_64
├── CMakeLists.txt
├── output
│ └── beqind-win32-x86_64
├── pkg
│ └── brainflow
├── scripts
│ └── build-win32-x86_64.bat
├── src
│ └── Main.cpp
└── .vscode
├── c_cpp_properties.json
├── settings.json
└── tasks.json
配置 CMake
基本上参考了官方示例,并额外修改了 CMAKE_CXX_STANDARD 和 brainflow_DIR。
cmake_minimum_required(VERSION 3.10)
project(beqind LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_VERBOSE_MAKEFILE ON)
macro(configure_msvc_runtime)
if(MSVC)
# Default to statically-linked runtime.
if("${MSVC_RUNTIME}" STREQUAL "")
set(MSVC_RUNTIME "static")
endif()
# Set compiler options.
set(variables
CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_MINSIZEREL
CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS_DEBUG
CMAKE_CXX_FLAGS_MINSIZEREL
CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_RELWITHDEBINFO
)
if(${MSVC_RUNTIME} STREQUAL "static")
message(STATUS
"MSVC -> forcing use of statically-linked runtime."
)
foreach(variable ${variables})
if(${variable} MATCHES "/MD")
string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}")
endif()
endforeach()
else()
message(STATUS
"MSVC -> forcing use of dynamically-linked runtime."
)
foreach(variable ${variables})
if(${variable} MATCHES "/MT")
string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}")
endif()
endforeach()
endif()
endif()
endmacro()
# link msvc runtime statically
configure_msvc_runtime()
set(brainflow_DIR "${CMAKE_CURRENT_SOURCE_DIR}/pkg/brainflow/lib/cmake/brainflow")
find_package(brainflow CONFIG REQUIRED)
add_executable(beqind
src/Main.cpp
)
target_include_directories(beqind PRIVATE
${brainflow_INCLUDE_DIRS}
)
target_link_libraries(beqind PRIVATE
# for some systems(ubuntu for example) order matters
${BrainflowPath}
${MLModulePath}
${DataHandlerPath}
${BoardControllerPath}
)
编译打包
脚本 build-win32-x86_64.bat 如下,运行流程是“设置 VS 环境-> 编译-> 打包”。
@echo off
chcp 65001
for %%i in ("%~dp0.") do set "script_dir=%%~fi"
for %%i in ("%script_dir%\..") do set "code_dir=%%~fi"
@REM set "default_vs_dir=K:\jc39\Program\FilesX86\Microsoft Visual Studio\2019\BuildTools"
set "default_vs_dir=K:\jc39\Program\Files\Microsoft Visual Studio\2022\Community"
if "%~1"=="" (set "vs_dir=%default_vs_dir%") else (set "vs_dir=%~1")
if not defined VisualStudioVersion (
call "%vs_dir%\VC\Auxiliary\Build\vcvarsall.bat" x64
)
set "build_dir=%code_dir%\build\win32-x86_64"
set "build_jobs=%NUMBER_OF_PROCESSORS%"
@REM set "build_generator=Visual Studio 16 2019"
set "build_generator=Visual Studio 17 2022"
rmdir /s /q "%build_dir%"
mkdir "%build_dir%"
cmake -S "%code_dir%" -B "%build_dir%" ^
-G "%build_generator%" -A x64 ^
-DCMAKE_BUILD_TYPE="Release"
cmake --build "%build_dir%" --parallel "%build_jobs%" --config "Release"
set "output_dir=%code_dir%\output\beqind-win32-x86_64"
rmdir /s /q "%output_dir%"
mkdir "%output_dir%"
robocopy "%build_dir%\Release" "%output_dir%" "beqind.exe"
robocopy "%code_dir%\pkg\brainflow\lib" "%output_dir%" "*.dll"
echo [+] Done
采集程序
采集源码
源码 Main.cpp 如下,运行流程是“连接-> 配置通道-> 采集数据-> 频带功率-> 预训练模型分析-> 简单公式分析-> 按‘q’停止采集-> 断连”。
#include <print>
#include <thread>
#include <memory>
#include "board_shim.h"
#include "data_filter.h"
#include "ml_model.h"
int main(int argc, char *argv[], char *envp[])
{
BoardShim::enable_dev_board_logger();
auto boardId = static_cast<int>(BoardIds::CYTON_DAISY_WIFI_BOARD);
BrainFlowInputParams inputParams;
inputParams.ip_address = "192.168.4.1"; // board IP
inputParams.ip_port = 10086; // host computer port
auto board = std::make_unique<BoardShim>(boardId, inputParams);
int res = 0;
try
{
board->prepare_session();
// https://docs.openbci.com/Cyton/CytonSDK/#channel-setting-commands
// x (CHANNEL, POWER_DOWN, GAIN_SET, INPUT_TYPE_SET, BIAS_SET, SRB2_SET, SRB1_SET) X
board->config_board("x1050110X"); // set gain to x12
board->config_board("x2050110X"); // set gain to x12
board->config_board("x3050110X"); // set gain to x12
board->config_board("x4050110X"); // set gain to x12
board->config_board("x5050110X"); // set gain to x12
board->config_board("x6050110X"); // set gain to x12
board->config_board("x7050110X"); // set gain to x12
board->config_board("x8050110X"); // set gain to x12
board->config_board("xQ161000X"); // power down
board->config_board("xW161000X"); // power down
board->config_board("xE161000X"); // power down
board->config_board("xR161000X"); // power down
board->config_board("xT161000X"); // power down
board->config_board("xY161000X"); // power down
board->config_board("xU161000X"); // power down
board->config_board("xI161000X"); // power down
board->start_stream();
auto samplingRate = BoardShim::get_sampling_rate(boardId);
auto eegChannels = BoardShim::get_eeg_channels(boardId);
std::println("eegChannels = [{}]", eegChannels);
auto windowSize = samplingRate * 2; // 2 seconds
auto sleepMs = 2000;
// pretrained model
BrainFlowModelParams mindModelParams(
static_cast<int>(BrainFlowMetrics::MINDFULNESS),
static_cast<int>(BrainFlowClassifiers::DEFAULT_CLASSIFIER));
MLModel mindModel(mindModelParams);
mindModel.prepare();
std::atomic_bool isRunning = true;
std::jthread inputThread([&isRunning]() {
char ch;
while (std::cin >> ch)
{
if ('q' == ch || 'Q' == ch)
{
isRunning = false;
break;
}
}
});
while (isRunning)
{
std::this_thread::sleep_for(std::chrono::milliseconds(sleepMs));
// for EEG channels, the data is returned in μV (microvolts)
auto boardData = board->get_current_board_data(windowSize);
// μV²
auto bandPower = DataFilter::get_avg_band_powers(boardData,
eegChannels, samplingRate, true);
std::println("bandPower, delta = [{}], theta = [{}], "
"alpha = [{}], beta = [{}], gamma = [{}]",
bandPower.first[0], bandPower.first[1],
bandPower.first[2], bandPower.first[3], bandPower.first[4]);
// predict by model
auto mindVec = mindModel.predict(bandPower.first, 5);
std::println("mindVec size = [{}], mindVec[0] = [{}]", mindVec.size(), mindVec[0]);
// predict by a simple formula, β / (α + θ)
auto attentionRatio = bandPower.first[3] / (bandPower.first[2] + bandPower.first[1] + 1e-6);
auto attentionScore = attentionRatio / (attentionRatio + 1.0);
std::println("attentionRatio = [{}], attentionScore = [{}]",
attentionRatio, attentionScore);
delete[] bandPower.first;
delete[] bandPower.second;
}
mindModel.release();
board->stop_stream();
board->release_session();
}
catch (const BrainFlowException &err)
{
BoardShim::log_message((int)LogLevels::LEVEL_ERROR, err.what());
res = err.exit_code;
if (board->is_prepared())
{
board->release_session();
}
}
return res;
}
运行结果
注意力集中样例:
bandPower, delta = [0.024791113588166075], theta = [0.03642246200472878], alpha = [0.029099243307624027], beta = [0.599255215185859], gamma = [0.31043196591362215]
mindVec size = [1], mindVec[0] = [0.9999999988536772]
attentionRatio = [9.145764240489672], attentionScore = [0.9014367004499075]
注意力不集中样例:
bandPower, delta = [0.10385765000510835], theta = [0.13881856846626758], alpha = [0.6301055786883959], beta = [0.09174414375008223], gamma = [0.03547405909014584]
mindVec size = [1], mindVec[0] = [6.379701400356172e-08]
attentionRatio = [0.11931479167975317], attentionScore = [0.10659627887227124]