TL;DR: чтобы получить интерфейс уровня BCI, не обязательно лезть в мозг. Нам часто достаточно читать следы намерения: мышцы, взгляд, жесты, голос, позу и физиологический контекст.
Эта статья выросла из ролика 8AAFFF с прекрасным названием I Built a Brain-Computer Interface (Without a Brain Scanner). Идея простая и очень инженерная: сделать интерфейс, который ощущается как BCI, но не пытается читать мозг напрямую.
Сначала я ожидал чего-то хардкорного: альтернативный биосигнал вместо EEG, куча фильтров, возможно, немного боли. Но автор сделал ход лучше: он убрал мозг из уравнения.
А нам правда обязательно читать мозг, чтобы понять намерение человека?
Может быть, достаточно читать следы намерения — в мышцах, взгляде, жестах, голосе, позе тела. Тогда задача превращается не в «прочитай мысли», а в: построить дешёвый, устойчивый и удобный контроллер человеческого намерения.
Это уже не совсем Brain-Computer Interface. Это Body-Computer Interface, Muscle-Computer Interface, Gaze-Computer Interface или, если собрать всё вместе, Human Intent Interface. Звучит скромнее, чем Neuralink на слайдах. Зато работает в реальности, а не только в питч-деке.
Почему классический BCI — это круто, но больно
BCI декодирует активность мозга в команды для компьютера. В медицине это огромное направление: нейропротезы речи уже синтезируют слова из мозговых паттернов почти в реальном времени, а парализованные пациенты получают канал коммуникации там, где раньше его не было.
Но если мы говорим о DIY-контроллере, AR/VR-интерфейсе, игре или pet-проекте, у BCI есть неприятные инженерные свойства:
- Сигнал шумный: EEG с поверхности головы ловит мышцы, моргания, движения и плохой контакт.
- Железо сложное: электроды, усилители, фильтрация, калибровка под пользователя.
- Пользователь устаёт: управление «силой мысли» часто менее естественно, чем выглядит на демо.
- Команд мало: получить много стабильных команд из неинвазивного EEG трудно.
- Цена ошибки высокая: если система решила нажать delete, пока ты просто задумался, это уже не интерфейс.
Поэтому вопрос «а что если управлять не мозгом?» не звучит как отказ от BCI. Это нормальный инженерный вопрос: обязательно ли лезть в самое шумное место, или можно поймать тот же сигнал на выходе?
Это как дебажить не внутренности фреймворка, а API-ответ. Внутри может быть ад, но если endpoint стабилен — мы живём.
1. Gesture Control: управление жестами через камеру
Самая очевидная альтернатива BCI — жесты руками. Камера видит руку, модель находит ключевые точки, а мы превращаем позу руки в команду. MediaPipe Hand Landmarker находит 21 точку на руке: пальцы, суставы, ладонь. Из этого уже можно собрать язык управления.
открытая ладонь → STOP
щипок пальцами → SELECT
палец вверх → CONFIRM
два пальца вперёд → NAVIGATE
движение руки вправо → NEXT
Python: распознаём pinch-жест через MediaPipe
Берём координаты кончиков большого и указательного пальцев. Если расстояние между ними ниже порога — это pinch.
import cv2, math
import mediapipe as mp
mp_hands = mp.solutions.hands
mp_draw = mp.solutions.drawing_utils
def euclidean(a, b):
return math.sqrt((a.x - b.x)**2 + (a.y - b.y)**2)
cap = cv2.VideoCapture(0)
with mp_hands.Hands(max_num_hands=1,
min_detection_confidence=0.7,
min_tracking_confidence=0.7) as hands:
while True:
ok, frame = cap.read()
if not ok:
break
frame = cv2.flip(frame, 1)
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
result = hands.process(rgb)
if result.multi_hand_landmarks:
lm = result.multi_hand_landmarks[0].landmark
dist = euclidean(lm[4], lm[8])
if dist < 0.04:
cv2.putText(frame, "PINCH: SELECT", (30, 60),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2)
mp_draw.draw_landmarks(
frame,
result.multi_hand_landmarks[0],
mp_hands.HAND_CONNECTIONS,
)
cv2.imshow("Gesture Controller", frame)
if cv2.waitKey(1) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
Уже почти BCI по ощущению: никакой клавиатуры, просто жест — и система реагирует. Но это не мозг. Дебажится через обычный print(), а не через молитву богам нейрофизиологии.
2. EMG / sEMG: управление через мышцы
EMG измеряет электрическую активность мышц. sEMG — поверхностная версия: электроды на коже, без имплантов. Когда ты хочешь пошевелить пальцем, мозг отправляет сигнал к мышцам. Мышцы активируются — и это можно измерить.
То есть мы не читаем мозг напрямую. Мы читаем намерение на выходе из нервной системы. Поэтому EMG-браслеты выглядят как очень практичный путь к «почти BCI» для AR/VR, ассистивных интерфейсов и тихого управления устройствами.
import serial, time
PORT = "/dev/ttyUSB0"
BAUD = 115200
THRESHOLD = 620
ser = serial.Serial(PORT, BAUD, timeout=1)
time.sleep(2)
while True:
line = ser.readline().decode("utf-8", errors="ignore").strip()
if not line:
continue
try:
value = int(line)
except ValueError:
continue
if value > THRESHOLD:
print("MUSCLE ACTIVATION → CLICK")
else:
print("relaxed")
Это учебная табуретка, но принцип правильный: мышечный сигнал → фильтр → порог или модель → команда.
Feature extraction + классификатор
from collections import deque
import numpy as np
from sklearn.ensemble import RandomForestClassifier
WINDOW = 64
buffer = deque(maxlen=WINDOW)
def extract_features(samples: list[float]) -> np.ndarray:
x = np.array(samples)
mav = np.mean(np.abs(x))
rms = np.sqrt(np.mean(x ** 2))
var = np.var(x)
return np.array([mav, rms, var])
X_train = [
[0.12, 0.18, 0.02],
[0.80, 0.91, 0.25],
[0.45, 0.50, 0.10],
]
y_train = ["rest", "fist", "pinch"]
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X_train, y_train)
features = np.array([[0.78, 0.88, 0.22]])
print(clf.predict(features)[0])
Для EMG классический ML часто выигрывает у тяжёлых архитектур: данных мало, паттерны хорошо разделимы, а latency важнее красоты модели.
3. Eye Tracking: управление взглядом
Мы часто смотрим туда, с чем хотим взаимодействовать. Eye-tracking давно используется в ассистивных технологиях: люди, которые не могут пользоваться руками, управляют компьютером глазами через системы вроде Tobii Dynavox.
Dwell-click: смотришь 0.7 секунды — срабатывает
import time
DWELL_TIME = 0.7
current_target: str | None = None
entered_at: float | None = None
def update_gaze(
gaze_x: float,
gaze_y: float,
buttons: dict[str, tuple[int, int, int, int]],
) -> str | None:
global current_target, entered_at
target = None
for btn_id, (x, y, w, h) in buttons.items():
if x <= gaze_x <= x + w and y <= gaze_y <= y + h:
target = btn_id
break
now = time.time()
if target != current_target:
current_target, entered_at = target, now
return None
if target is not None and now - entered_at >= DWELL_TIME:
return f"CLICK:{target}"
return None
Главная ловушка — Midas Touch: человек смотрит на объект, но это не всегда значит, что хочет нажать. Решение простое: взгляд выбирает, другой сигнал подтверждает.
gaze_target = "send_button"
emg_action = "PINCH"
if gaze_target and emg_action == "PINCH":
execute(f"SELECT:{gaze_target}")
4. EOG: глаза как биологический джойстик
EOG измеряет электрический потенциал при движении глаз. Камера не нужна: используются электроды вокруг глаз. Влево, вправо, вверх, вниз, моргание, двойное моргание, зажмуривание — минимальный набор команд уже есть.
def classify_eog(horizontal: float, vertical: float, blink: float) -> str:
if blink > 0.8:
return "BLINK"
if horizontal > 0.5: return "LOOK_RIGHT"
if horizontal < -0.5: return "LOOK_LEFT"
if vertical > 0.5: return "LOOK_UP"
if vertical < -0.5: return "LOOK_DOWN"
return "CENTER"
Это полезно для простых меню, ассистивных интерфейсов, управления инвалидной коляской и hands-free режимов — особенно там, где камера недоступна или нежелательна.
5. Head Tracking: голова как контроллер
Голова тоже может быть контроллером: кивок → да, поворот → нет или назад, наклон → навигация. Реализуется через webcam, IMU, VR-шлем или смартфон.
def classify_head_pose(yaw: float, pitch: float, roll: float) -> str:
if yaw > 25: return "TURN_RIGHT"
if yaw < -25: return "TURN_LEFT"
if pitch > 20: return "LOOK_DOWN"
if pitch < -20: return "LOOK_UP"
if abs(roll) > 20: return "TILT"
return "CENTER"
Плюсы: дёшево, понятно, хорошо комбинируется с gaze и voice. Минусы: устаёт шея, мало команд, в общественном месте выглядишь так, будто споришь с невидимым налоговым инспектором.
6. Voice Control + LLM: голос как интерфейс намерения
Голос — самый недооценённый альтернативный BCI. Он не читает мозг, но иногда читает намерение лучше любого жеста.
микрофон → speech-to-text → intent parser → команда
естественная фраза → LLM выделяет намерение → система вызывает API
from dataclasses import dataclass
@dataclass
class Intent:
action: str
target: str | None = None
params: dict | None = None
def parse_command(text: str) -> Intent:
t = text.lower()
if any(w in t for w in ("останов", "стоп", "stop")):
return Intent(action="STOP")
if "следующ" in t or "next" in t:
return Intent(action="NEXT")
if "назад" in t or "back" in t:
return Intent(action="BACK")
if "открой" in t and "заяв" in t:
return Intent(action="OPEN", target="leads")
return Intent(action="UNKNOWN", params={"raw": text})
LLM-based intent parser
SYSTEM_PROMPT = """
Ты получаешь команду пользователя и контекст интерфейса.
Верни ТОЛЬКО валидный JSON без пояснений и markdown-блоков.
Контекст:
current_screen: crm.leads
selected_object: leads_table
available_actions: [filter, sort, open, create_task]
"""
USER_COMMAND = "Покажи только горячих и отсортируй по дате"
Голос хорош для сложных задач: «собери отчёт по новым лидам за неделю». Жесты и EMG — для быстрых атомарных команд: выбрать, отмена, вперёд. Вместе они закрывают весь диапазон.
7. IMU: акселерометр и гироскоп
IMU можно поставить в браслет, кольцо, перчатку, наушники или телефон. Он дешёвый, понятный и достаточно стабильный для жестов движения.
#include <Wire.h>
#include <MPU6050.h>
MPU6050 mpu;
void setup() {
Serial.begin(115200);
Wire.begin();
mpu.initialize();
}
void loop() {
int16_t ax, ay, az, gx, gy, gz;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
if (gx > 12000) Serial.println("GESTURE:TURN_RIGHT");
else if (gx < -12000) Serial.println("GESTURE:TURN_LEFT");
else if (ay > 14000) Serial.println("GESTURE:TILT_UP");
delay(20);
}
8. GSR, пульс, дыхание: не команды, а контекст
Некоторые сигналы плохо подходят для прямого управления, но хорошо описывают состояние пользователя.
GSR → стресс / возбуждение
пульс → нагрузка / тревога
дыхание → спокойствие / паника
температура кожи → физиологический контекст
def estimate_stress(
heart_rate: float,
gsr: float,
breathing_rate: float,
) -> float:
score = 0.0
if heart_rate > 100: score += 0.4
if gsr > 0.7: score += 0.4
if breathing_rate > 22: score += 0.2
return min(score, 1.0)
stress = estimate_stress(heart_rate=108, gsr=0.82, breathing_rate=24)
if stress > 0.7:
print("Switch UI to calm mode")
Если добавить эмоцию из текстового канала, получится мультимодальный emotional state: LLM может менять тон, длину ответов и уровень риска в зависимости от состояния пользователя.
9. Гибридный контроллер: нормальный путь
Главная ошибка — пытаться выбрать один идеальный способ управления. В реальности выигрывает гибрид.
глаза → куда пользователь смотрит (selection)
жест → что он хочет сделать (action)
EMG → подтверждение микродействия (confirmation)
голос → сложная команда (intent)
пульс/GSR → эмоциональный контекст (modulation)
LLM → интерпретация намерения (policy)
Event Bus: единый поток событий
from dataclasses import dataclass, field
from time import time
from typing import Callable
@dataclass
class InputEvent:
source: str
event: str
confidence: float
timestamp: float = field(default_factory=time)
metadata: dict = field(default_factory=dict)
class EventBus:
def __init__(self):
self._subs: list[Callable[[InputEvent], None]] = []
def subscribe(self, callback: Callable[[InputEvent], None]) -> None:
self._subs.append(callback)
def publish(self, event: InputEvent) -> None:
for cb in self._subs:
cb(event)
Fusion logic: приоритеты сигналов
from dataclasses import dataclass
@dataclass
class SignalState:
gaze_target: str | None
gesture: str | None
emg_action: str | None
voice_intent: str | None
stress: float
def decide_action(s: SignalState) -> str:
if s.voice_intent == "STOP" or s.gesture == "OPEN_PALM":
return "STOP_CURRENT_ACTION"
if s.gaze_target and s.emg_action == "PINCH":
return f"SELECT:{s.gaze_target}"
if s.voice_intent == "OPEN_LEADS":
return "OPEN_VIEW:CRM_LEADS"
if s.stress > 0.75:
return "ASK_CONFIRMATION"
return "NO_ACTION"
10. Где здесь LLM?
LLM не должен распознавать жесты. Для этого есть CV-модели, фильтры и классификаторы. LLM нужен выше уровнем: политика, планирование, объяснение и контекстные действия.
Низкий уровень: EMG / gaze / gesture / voice
Средний уровень: classifier / fusion / intent detection
Высокий уровень: LLM policy / planning / explanation / context-aware action
Пример: пользователь смотрит на таблицу клиентов, делает pinch и говорит «оставь только горячих». LLM связывает контекст интерфейса, голосовую команду, историю действий и возвращает структурированный action.
{
"action": "filter",
"target": "leads_table",
"filters": {"temperature": "hot"}
}
11. Этика: безопаснее, но не автоматически хорошо
Альтернативы BCI менее пугающие, чем чтение мозга, но это не делает их безобидными. Взгляд показывает интерес, пульс — реакцию, GSR — стресс, голос — эмоции, жесты — привычки, EMG — моторные паттерны.
- Локальная обработка по умолчанию.
- Минимум хранения сырых сигналов.
- Явное согласие пользователя.
- Возможность выключить каждый сенсор.
- Логи действий, а не логи тела.
- Никакого скрытого emotional profiling.
Иначе получится не интерфейс будущего, а очередная рекламная мясорубка: «мы поняли, что вы нервничаете, поэтому вот кредит под 49%».
Итоговое сравнение
| Подход | Источник сигнала | Странность | Практичность | Риск приватности |
|---|---|---|---|---|
| EEG / BCI | Нейронная активность | Высокая | Сложно | Очень высокий |
| EMG / sEMG | Мышцы | Средняя | Высокая | Средний |
| Eye tracking | Взгляд | Средняя | Высокая | Средний |
| Gesture control | Движение руки | Низкая | Высокая | Низкий |
| Voice + LLM | Речь | Средняя | Высокая | Средний |
| GSR / Pulse | Физиология | Средняя | Средняя | Средний |
| Hybrid Intent | Мультимодальный поток | Зависит от дизайна | Очень высокая | Управляемый |
Вывод
Самая сильная идея здесь — отказ от фетиша «читать мозг любой ценой». Мозг — не единственная точка входа. Намерение проявляется во взгляде, мышцах, жестах, голосе, позе, дыхании.
Если собрать эти сигналы вместе, получается интерфейс, который ощущается почти как BCI, но дешевле, проще, доступнее, безопаснее и лучше подходит для DIY и pet-проектов.
Не «компьютер читает мысли». А «компьютер понимает, что я пытаюсь сделать». Честно говоря, это даже круче.
Потому что чтение мыслей красиво на слайдах. А понимание намерения — это то, чем реально можно пользоваться.