JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

The Velocity Kalman Filter: Optimal Real-Time Kinematic Decoding

Build the workhorse decoder of intracortical motor BCI from first principles: a linear-Gaussian state-space model estimated recursively, one bin at a time.

Movement intention as a hidden state

Model the thing you want as a hidden state \mathbf{x}_k — for a cursor, position and velocity, \mathbf{x} = [x, y, v_x, v_y, 1]^\top (the trailing 1 absorbs baseline offsets). The neural features you measure are the observation \mathbf{z}_k — the vector of binned firing rates. A state-space model couples them with two linear-Gaussian equations.

\begin{aligned} \mathbf{x}_k &= A\,\mathbf{x}_{k-1} + \mathbf{w}_k, & \mathbf{w}_k &\sim \mathcal{N}(\mathbf{0}, W) \\ \mathbf{z}_k &= C\,\mathbf{x}_k + \mathbf{q}_k, & \mathbf{q}_k &\sim \mathcal{N}(\mathbf{0}, Q) \end{aligned}

The trajectory (state) model above, the observation (encoding) model below. Everything the decoder knows lives in A, W, C, Q.

The Kalman filter models movement as a hidden state that glides forward in time (the trajectory model) and generates the neural firing you observe (the observation model). Everything the decoder assumes about the world is packed into four matrices.

\mathbf{x}_k
The hidden state (e.g. cursor velocity) at step k.
\mathbf{z}_k
The observed neural firing at step k.
A,\ W
State transition and its process-noise covariance.
C,\ Q
How state maps to neural observations, and the observation noise.

A says "velocity persists"; C says "these neurons fire this much for that velocity" — the velocity Kalman filter.

The two models: dynamics and encoding

The trajectory model A is a smoothness prior — a near-random-walk that says velocity persists and is mildly damped; W sets how much the state may jump per bin. The observation model C is the encoding map: each row is one channel's linear tuning to kinematics, so C\mathbf{x} is the firing rate this state predicts, and Q is the neural noise covariance. Both are fit from a short calibration block of observed or attempted movements.

The predict–update recursion

Each bin runs two steps. Predict: push the last estimate through the dynamics and grow the uncertainty. Update: pull the prediction toward the new neural measurement, weighted by how much you trust it.

\hat{\mathbf{x}}_k^- = A\,\hat{\mathbf{x}}_{k-1}, \qquad P_k^- = A\,P_{k-1}\,A^\top + W

Predict step: a priori state and its covariance.

Before seeing new neural data, the filter guesses where the state went by rolling the last estimate forward through the dynamics, and it grows its uncertainty to reflect that a prediction is never perfect.

\hat{\mathbf{x}}_k^-
The a-priori (predicted) state, before the measurement.
A\,\hat{\mathbf{x}}_{k-1}
Last estimate pushed forward by the dynamics.
P_k^-
The predicted uncertainty (covariance).
W
Process noise added, inflating the uncertainty.

"Last frame the cursor moved right, so predict it keeps moving right" — but with wider error bars.

\begin{aligned} K_k &= P_k^-\,C^\top\!\left(C\,P_k^-\,C^\top + Q\right)^{-1} \\ \hat{\mathbf{x}}_k &= \hat{\mathbf{x}}_k^- + K_k\,\underbrace{\left(\mathbf{z}_k - C\,\hat{\mathbf{x}}_k^-\right)}_{\text{innovation}} \\ P_k &= \left(I - K_k C\right)P_k^- \end{aligned}

Update step: the Kalman gain weights the innovation (measurement residual) against the prior.

Now the new neural measurement arrives. The filter compares it to what it expected (the innovation) and nudges its prediction toward the data by an amount set by the Kalman gain — trusting the measurement more when the neural signal is reliable, the prediction more when it is not.

K_k
The Kalman gain — the trust dial between prediction and measurement.
\mathbf{z}_k - C\,\hat{\mathbf{x}}_k^-
The innovation: actual observation minus what was expected.
\hat{\mathbf{x}}_k
The corrected (posterior) state estimate.
P_k
The reduced uncertainty after using the measurement.

If neurons fire more than the "moving right" prediction expected, the Kalman gain revises the velocity upward.

The Kalman gain K_k is the whole story of trust: when neural noise Q is large it shrinks and the filter leans on its prior (smooth, laggy); when Q is small it grows and the filter chases the measurement (responsive, jittery). The innovation \mathbf{z}_k - C\hat{\mathbf{x}}_k^- is the new information; if your model is right it should be white noise, which is also a live diagnostic that the encoding model has drifted.

# one closed-loop bin k of a velocity Kalman filter
x_pred = A @ x                                  # predict state (pos, vel)
P_pred = A @ P @ A.T + W
S      = C @ P_pred @ C.T + Q                   # innovation covariance
K      = P_pred @ C.T @ inv(S)                   # Kalman gain
x      = x_pred + K @ (z - C @ x_pred)           # correct with firing rates z
P      = (I - K @ C) @ P_pred
cursor_velocity = x[2:4]                         # drive the effector
# user sees the cursor move, adjusts intention -> next z
The full recursion is a handful of matrix ops per 20–50 ms bin — cheap enough for hard real time.

Tuning the filter is tuning the feel

There is no free lunch between smoothness and lag. Larger process noise W (or smaller Q) yields a responsive but twitchy cursor; the reverse yields a smooth but sluggish one. In practice you tune the ratio W/Q against the user's felt control, not against offline error — the best offline fit is often too laggy to fly.

Drive a cursor to targets while noisy 'intended velocity' feeds a velocity Kalman filter. Push the process- vs measurement-noise sliders and feel the smoothness–lag tradeoff the gain equation describes.

Where the clean model breaks

This filter is optimal only under its assumptions, and real cortex violates them. Tuning C drifts within and across sessions; integrated velocity accumulates position bias; and the calibration was collected open-loop, so its statistics differ from the closed-loop regime the decoder will actually run in. Each failure has a fix — and each fix is the subject of the next guide.