EMG signal processing for bruxism detection

Hi everyone.
I've been playing with and Arduino Uno R4 WiFi and the OLIMEX-EKG-EMG shield.
I would appreciate some guidance in detecting the muscle signal properly.

Mods please feel free to relocate this post if this is the wrong category.

What I did:

  • FFT of the analog signal input using the ArduinoFFT library
  • Filtered out frequencies 0 to 60Hz
  • Sent the data over to Processing for DSP and logging

What I can't seem to accomplish:

  • Detect the jaw clenching reliably (current detection isn't continuous and it's prone to false positives, must be re-done from scratch)
  • Setting beeps and alarms to stop clenching, this is more difficult than expected because of the unreliable detection

Must mention:

  • I'm reusing the electrodes, the gel might not be fresh and that can become a problem soon.

What is the recommended approach to detect the signal in the FFT?
We're dealing with nonlinear signals, so I don't really know what I'm aiming for

Spectrogram of mostly resting jaw
Spectrogram of mostly resting jaw

Clenching and holding a couple times
Clenching and holding a couple times

Spectrogram including the frequencies 0 to 60Hz + some clenching
Spectrogram including the frequencies 0 to 60Hz + some clenching

The following snippet illustrates how the vReal float array (from the examples of ArduinoFFT) is populated before sending it to Processing to draw the spectrogram.

float vReal[samples]; // We're working with this array, of size samples/2

ArduinoFFT<float> FFT = ArduinoFFT<float>(vReal, vImag, samples, samplingFrequency);

. . . . . .


void collect_samples() {
  for (uint16_t i = 0; i < samples; i++) {
    vReal[i] = analogRead(analog_pin);
    vImag[i] = 0.0;
    delayMicroseconds(1000000 / samplingFrequency);
  }
}

. . . . . .

void loop() {
  collect_samples();

  FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward);
  FFT.compute(FFTDirection::Forward);
  FFT.complexToMagnitude();

  // Erasing all bins up to 60Hz (the shield has a filter)
  uint8_t i = 255;
  while (freq_bin * (++i) <= 60)
    vReal[i] = 0;

  send_to_udp();
  
 }

The full code is a mess, partially done with GPT too. Needs to be rewritten as mentioned. I can still post it if needed.

(This post is also on reddit)

The FFT result is simply a different representation of the same signal, but in the frequency domain.

What are you expecting to see, and how does the FFT help?

Thank you for responding. The FFT is supposed to show when the muscle is activated (the signal is mostly concentrated between 50 and 150Hz).

What I am missing is a way to detect it properly, continued clenching being the hardest case because the signal seems to subside.. In the second gif I held the jaw contracted for while, you can see the signal is subtle in that phase
image

Might be relatively easy to detect, I'm new to this kind of processing.

I doubt that, but it will be a good learning experience. Have fun!

1 Like

SVM mostly did the trick. It's not perfect, but it was relatively easy indeed.

This project is now public on GitHub

SVM like in Support Vector Machine ?

Exactly. Have a look, this runs on the Uno R4

static const float weights[] = {
  0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,-0.16666446,0.14036143,0.10275731,0.15018884,0.17533556,-0.01297485,0.29788694,-0.10588868,0.16164895,0.07309278,-0.06163993,0.72166246,-0.05186196,0.05293969,0.17246698,-0.09546843,0.25317097,-0.05021467,0.20567502,0.12985801,0.05549219,0.18976886,-0.10753195,-0.29549880,0.30059777,0.24690098,0.00517570
};
static const float bias = -0.2237529517562951;
static const int weight_length = (sizeof(weights) / sizeof(float));

static_assert(samples / 2 == weight_length, "Error: Weights are not as many as samples/2");

int classify(float input[]) {
  float sum;
  arm_dot_prod_f32(input, weights, weight_length, &sum);  // SIMD-optimized dot product
  sum += bias;

  return sum >= 55 ? 1 : 0;  // Classification threshold
}

That's cool :slight_smile: I used SVM for ECG/HRV classifications a decade ago, was quite a nice thing to do.

Can this be used to detect snoring or heart rate in some way? I don't expect to do so with only one emg sensor, but at least snoring could be detected using a microphone