BrainFlow 库的使用方法

jc39
jc39
发布于 2026-06-24 / 2 阅读
0
0

BrainFlow 库的使用方法

官网:BrainFlow

API :User API — BrainFlow documentation

示例:Code Samples — BrainFlow documentation

仓库:brainflow-dev/brainflow

编译安装

脚本 build-brainflow-5.22.2.bat 如下,运行流程是“设置 VS 环境-> 解压-> 编译安装-> 修改绝对路径”。

需要把 brainflow-5.22.2.tar.gz 与脚本放在同一目录,且按需替换 vs_dirbuild_generatoroutput_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_STANDARDbrainflow_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]

评论