The problem
A consumer headband provides only a few EEG channels. Blinks, jaw movement, electrode contact, and changes in attention all affect the recording. A usable control pipeline must turn that changing signal into features that can be collected consistently and classified repeatedly.
MCX explores five labels: relax, concentrate, jaw clench, blink, and a null state. Those labels include both mental tasks and physiological actions. The drone project provides the application context; the accompanying paper evaluates the shared interface and demonstrates a robotic-arm application.
My contribution
I designed the BCI, built the EEG preprocessing and feature pipeline, trained the command classifier, and implemented real-time socket-based control. I worked on connecting the signal-processing experiments to an interface that could collect data, train models, and pass predictions to a program.
Architecture
The pinned implementation separates collection, preprocessing, model management, and application callbacks. LSL carries the live data into the reader. The preprocessing classes create the representations expected by the selected model and reuse its saved scaler during inference.
Two model families share this path. The averaging model flattens mean bandpowers into a dense classifier. The time-series model preserves a sequence of band-by-electrode arrays for convolutional and recurrent layers.
EEG processing
The paper records 256 Hz sampling and one-second Welch windows advanced every 0.1 seconds. Power is aggregated across delta, theta, alpha, beta, and gamma bands. The source requests spectral estimates between 0.5 and 50 Hz, then applies the configured band boundaries.
This illustration combines a 10 Hz sinusoid with a smaller 20 Hz component. It makes the relationship between the time trace and frequency bands visible. The recording pipeline processes measured headband samples through Welch estimation.
segment = samples_data[sample_idx, start:end, ch_idx]
psd, freqs = mne.time_frequency.psd_array_welch(segment, sfreq=self.SFREQ, fmin=0.5, fmax=50, n_fft=self.SFREQ, verbose=0)
for band_idx, (band_name, (fmin, fmax)) in enumerate(self.BANDS.items()):
band_mask = (freqs >= fmin) & (freqs < fmax)
bandpowers[sample_idx, band_idx, ch_idx, int(start//(self.SFREQ*self.UPDATE_S))] = np.trapezoid(psd[band_mask])The temporal formatter applies a log transform, averages short groups of bandpower frames, and collects them into prediction windows. Standardization puts those features on a common scale. The paper’s real-time configuration evaluates the preceding five seconds of data once per second.
Classification and control
The CNN-LSTM treats each timestep as a frequency-band-by-electrode array. TimeDistributed convolution and pooling extract relationships within that array. Two LSTM layers then summarize how those relationships evolve, and dense layers produce the class distribution.
self.model = Sequential([
layers.Input(self.inp_shape + (1,)),
layers.TimeDistributed(layers.Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding='same')),
layers.TimeDistributed(layers.MaxPooling2D(pool_size=(2, 2))),
layers.TimeDistributed(layers.Conv2D(filters=64, kernel_size=(3, 3), activation='relu', padding='same')),
layers.TimeDistributed(layers.MaxPooling2D(pool_size=(2, 2))),
layers.TimeDistributed(layers.Flatten()),
layers.LSTM(64, return_sequences=True),
layers.LSTM(64, return_sequences=False),
layers.Dense(64, activation='relu'),
layers.Dense(32, activation='relu'),
layers.Dropout(0.3),
layers.Dense(self.num_classes, activation='softmax')
])The current robotic-arm adapter translates repeated predictions into gripper, elbow, wrist, and reset actions. A separate orientation callback supplies head-direction input. This application boundary lets the classifier feed a program with its own device commands.
The opening’s drone follows a fixed illustrative route. Its timing is authored for the page; the paper’s measured results concern state classification and its robotic-arm control interface.
Evaluation
Table 1 of the paper reports 76% offline accuracy across 1,266 test windows. Performance varies across the five classes: jaw clench has an F1 score of 0.97, while concentrate has an F1 score of 0.56.
Table 2 records 47 correct decisions across 50 real-time trials, giving 94%. There were ten trials per class. Each decision used the mode of five predictions after approximately five seconds of task performance. The relaxation trials used closed eyes, while the training procedure had used open eyes.
These results describe the paper’s evaluation procedure. Its overlapping-window construction and buffered trial decisions are relevant when interpreting the offline and real-time numbers. The paper also discusses eye-movement confounding in its separate motor-imagery experiment.
Paper
On the Efficacy of a Consumer-Grade EEG Device for Brain-Computer Interface Applications, by Carter Nebrich and Michael Rusu, May 2025.
Read the MCX paper ↗