← Back to blogs
NeuroTech @ Berkeley · Jan 2026 — Present

NeuroTech EEG Gait Decoder

This was one of the projects I was most excited for coming into it. When I was applying to colleges, I was specifically looking for schools doing active research in brain-computer interfaces and neuroprosthetics, so getting to work on a gait-decoding project felt like coming full circle.

The goal itself sounds almost like science fiction: read someone’s brain activity and infer how their leg is moving. The payoff is obvious — better prosthetics, exoskeletons, and rehabilitation tools that respond to intent rather than residual muscle signals.

The catch is that most brain-computer interface work that actually decodes movement well relies on invasive, intracranial electrodes. That works in a lab, but it is hard to deploy. So the concrete research question we set out to answer was narrower and harder: given noisy, non-invasive EEG recordings, can we decode gait information quickly and reliably enough to be useful to a downstream assistive controller?

EEG Data Is Not Fun

EEG is an ML engineer’s worst nightmare. The signal is tiny, noisy, and buried under eye blinks, muscle artifacts, and electrical interference. On top of that there is extreme subject-to-subject variation — and even recordings from the same person drift between sessions.

Cleaning Up The Signal

Before any modeling, we built the standard EEG preprocessing pipeline:

  • Band-pass filtering to keep the frequency ranges that carry movement-related activity
  • Notch filtering to kill electrical line noise
  • Normalization across channels
  • Walking detection to keep only the windows where the subject was actually walking
  • Windowing into fixed 1-second segments

We also generated labels from the motion capture. Heel-strikes were approximated from peaks in the smoothed ankle trajectory, and gait phase came from splitting each stride into bins.

Getting A Baseline

At first we framed the project the cleanest way possible: direct continuous regression. Take an EEG window, predict the exact lower-limb joint angle. It is the most natural way to state the problem, and as we found out, also the most brittle.

We started where almost every neuro project starts: a simple CNN-LSTM. The CNN pulls spatial features off the electrode array, the LSTM models how they evolve over time. It is the workhorse baseline for this kind of task, so it was the fastest way to find out whether there was any signal at all.

The results were not good. The model was almost exclusively predicting the mean angle outputting roughly the average trajectory regardless of the input.

This was a specific and telling failure. When a regressor collapses to the mean, it has decided the cheapest way to lower its loss is to hedge toward the average rather than track the real signal. In other words, it found nothing learnable in the EEG and gave up tracking the leg. We had a lot of experimenting ahead of us.

The Dataset Problem

The first thing we noticed was the data. Our initial dataset had only 2 subjects and a couple of sessions each. That was enough to get the pipeline working but nowhere near enough to say anything about generalization. Performance sometimes didn’t hold up even within the same subject across sessions.

So we went looking for something bigger. We settled on a public mobile brain–body imaging dataset: 8 subjects, multiple treadmill-walking trials each, 64-channel EEG, and synchronized lower-limb joint angles. Far more diverse, and far more data to actually learn from.

What We Tried

We split up and each took a different approach, then came back to compare results.

One team tried a self-supervised autoencoder feeding a CNN-BiLSTM — compress EEG windows into a clean latent without labels, then predict from that representation.

Another team tried a 1D ResNet. It had the capacity to fit the training data easily, but its validation loss showed it was overfitting rather than learning robust gait-related EEG structure.

The last team implemented a very specific CNN architecture from this paper — older, but well-established for EEG, and we didn’t want to rule it out without testing it.

ModelTrain Loss (MSE)Val Loss
Raw CNN-LSTM1.0060.985
1D ResNet0.1731.125
Autoencoder + CNN-BiLSTM0.1860.961

Across every approach, the same pattern held: we could fit the training data, but exact angle regression didn’t generalize very well. That pushed us to the realization that reshaped the whole project.

A few realizations

We were still trying to predict ankle angle, but angle could not be the only supervision signal. If the model only saw continuous angle loss, it tended to learn a weak average trajectory instead of a useful gait representation.

The better framing was multi-task: predict gait timing signals alongside the angle. Heel-strike and gait phase are more stable labels, and asking the model to predict them helped shape the shared representation that the angle regressor depended on.

The other realization was about how we fed gait phase to the model. Phase is circular: the end of one stride is the start of the next, so representing it as a plain class number creates an artificial jump between the last phase and the first. Converting phase into sine and cosine features instead gives the model a smooth, continuous sense of where the subject is in the walking cycle. This turned out to be one of the changes that helped angle prediction the most.

As we will see this set of changes worked really well together.

The Final Model

The final design is a multi-task EEG gait-decoding model trained separately for each subject. For each subject, we split the data temporally into an 80% training segment and a 20% validation segment, and left a 5-second gap between them to reduce temporal leakage.

The input is a 1-second EEG window from the 64 EEG channels. Each EEG window gets labels from the middle time point of the window: heel-strike label, gait-phase label, smoothed joint angle, and timestamp.

The core model is a Temporal Convolutional Network. The TCN processes the EEG window and creates a shared latent representation. From that representation, the model branches into multiple heads and returns:

  • heel_logit: binary heel-strike output, converted to probability with sigmoid
  • phase_logit: 4-class gait-phase prediction
  • angle_pred: predicted joint angle

The angle branch uses the shared TCN latent vector plus the cyclic (sine/cosine) encoding of gait phase from above, fed into an LSTM.

Heel-strike plays a quieter role. The predicted heel-strike probability is not fed into the angle branch. It helps indirectly by shaping the shared TCN representation during training. In plain language: heel-strike prediction teaches the model where it is in the walking rhythm, and the angle predictor can reuse that timing-aware representation.

Training It In Stages

Training happens in three stages:

  1. TCN classification. Train the TCN on heel-strike detection and gait-phase classification. Heel-strike is emphasized because strikes are rare: heel_weight = 2.0, and the binary heel-strike loss uses a positive-class weight of 5.0.
  2. Angle regression. Freeze the TCN/classification parts and train the LSTM angle branch to predict joint angle from the learned representation and cyclic phase encoding.
  3. Full fine-tuning. Unfreeze everything and fine-tune end to end with all three losses: heel-strike, phase, and angle.

This means heel-strike influences the shared encoder during the first stage and again during final fine-tuning, even though heel-strike probability is never handed directly to the angle regressor.

Where It Landed

The strongest result was heel-strike detection. Across most subjects (6 of 8) the TCN reached a mean AUC of 0.924 — versus a logistic-regression baseline around 0.57.

For 4-class gait phase, the model hit 72.8% mean accuracy on those subjects, where chance is 25%. Continuous ankle-angle regression stayed the hardest task, but the staged LSTM finally produced meaningful predictions: a mean R² of 0.735 across 5 of 8 subjects.

The spread across that “6 of 8” and “5 of 8” is the honest headline. One subject’s heel-strike AUC collapsed to near zero, and a couple of others lagged the rest. That inter-subject variability — not the average — is the real frontier here, and lines up with what the literature reports: non-invasive decoders are still fragile across people.

The main lesson was that EEG gait decoding is not just a model-architecture problem. The dataset, the preprocessing, how you define the target, and how you split for evaluation matter just as much. Direct angle prediction from non-invasive EEG was brittle on its own, but predicting gait events and phase alongside angle helped the model learn a representation strong enough to make the angle head useful.