Implementing a “headset in use” vs. “headset not in use” feature

RomainPRomainP Belgium
edited April 2023 in Software

Hello all, I’m trying to implement a real-time feature (using brainflow in Python) to detect if my headset (ultracortex mark iv, cyton) is “in use” (turned on, on the user’s head, with clips attached to earlobes) or “not in use” (turned on, but at rest on a table, clips not attached to anything).

I’ve tried multiple ways to achieve this:

  1. Using the railing values: this does not work because while turning on the headset at rest all railing values are at 100%, after about 2 min. most channels get unrailed.
  2. Calculating the moving average over a time_window of 2s on each channel, then do a standard deviation analysis on the last 20 time_windows. The idea is that at rest fluctuations in the signal are minimal, so if the std dev of the moving average values remain below a certain threshold I can consider the headset to be “at rest”. Problem with this is that free pins are extremely sensitive to perturbations (due to the amplification and unconnected ref), so just by bumping into the table or waving my hand around the headset without touching it, fluctuations appear with an amplitude equivalent to the “in use” case.
  3. Using the accelerometer_data : it’s pretty easy to detect if the headset is barely moving. Problem is that I can sit still enough with the headset on my head to basically obtain the same acceleration values than at rest.
  4. Using the band power values for a frequency range of [40, 50] Hz (I used the OpenBCI GUI spectrometer to find the frequency range where “in use” and “not in use” are the most differentiated). Problem is that this is also pretty sensitive to perturbations.

So none of these methods worked. I’ve got a couple of further ideas, but I’d like to have your opinion on them before implementing them, for various reasons:

  • Using reference values directly: could theoretically be a good idea, but I can’t find a way to get these values separately from the cyton board. And even if I found a way to get these values, it could be just as sensitive to perturbations as case 2.;

  • Use a neural network taking as input railing values, standard deviation of the moving average over the last time_window, accelerometer data, and band power, and training it to detect if the headset is “in use” or “not in use”. While this could work, this represents a LOT of work, so I prefer first considering easier methods to implement.

So my questions to you are:

  • Are there any ready-made scripts out there doing this?
  • If not, are there easier ways to do this?
  • If not, do you know of any other methods I could try out (based on other parameters perhaps)?

I started programming this feature thinking it would be done in a couple of hours, but it turned out to be way more difficult than I expected. So any ideas, feedback on the above, help, tips, or even ready-made solutions would be greatly appreciated. Thanks in advance for your help!

Comments

  • wjcroftwjcroft Mount Shasta, CA

    Hi Romain,

    Have you considered using the impedance checking mode?. Channel impedances when touching skin are markedly different than when not touching. The forehead channels (Fp1 or Fp2) are good candidates, since these have larger contact surfaces than the combs.

    https://docs.openbci.com/Cyton/CytonSDK/#leadoff-impedance-commands

    You want to be measuring the N-channel input, since this is the default wiring on Cyton. Pins closest to board surface.

    It might be wise to do a combination of this with the accelerometer approach. Accelerometer moving would be your primary indicator of usage. If accelerometer becomes very still, no movement, check the impedance periodically to confirm it is still on the head.

    Regards, William

  • Hello William,

    Thanks a lot for your suggestion, this indeed seems to be a viable way to achieve this detection feature!
    With the impedance check I have about 500kOhm when in use, and 5000kOhm when not in use, and unlike the railing values this difference remains stable over time. But I have some questions concerning this impedance check:

    1. It's pretty logical for difference in impedance in the "in use" and "not in use" cases to remain stable over time, and it's effectively what I observe here. What I don't understand is why the railing percentage (live impedance in the "Cyton signal" widget of OpenBCI GUI 5.10), which I expect to be directly linked to absolute impedance values, doesn't. Why is it that if I leave my headset running empty, all railing percentages start at 100%, and then gradually all go down to low values? Shouldn't it remain at 100%? If I had to guess I'd say it's because the math behind the calculation of the railing percentage is only adapted for live sessions on someone's head, and it normalizes the impedance values w.r.t. the other channels to calculate the railing percentage (basically meaning the railing percentage has never been designed for cases where the headset isn't on a user's head). Is this guess correct?
    2. While doing an impedance check works great to differentiate the "in use" and "not in use" cases, injecting the 31Hz in any channel perturbs all other channels as well, for a couple of seconds at least. This isn't great for real-time applications, as I can't just periodically check the impedance without perturbing the real-time signal. I guess this is why you proposed to first use the accelerometer data to determine if there's a lack of motion, and then only perform an impedance check, right? The solution would be to directly get the 'live absolute impedance values' (= actual kOhm values) rather than 'live relative values' (= the railing percentages), and just apply a threshold to those (i.e.: if live absolute impedance of Fp1 and Fp2 >3000kOhm ==> headset is "not in use"), what do you think?
    3. I'm using Brainflow on Python, which doesn't have a method to get the absolute impedance values from a cyton board (it only has a function to get the relative railing values). Could you suggest a way for me to get these absolute values? Maybe by parsing the serial stream in parallel to the brainflow parsing? If yes, do you have some useful examples/links to be able to implement this in python?

    Thanks for all the fish the help!

  • wjcroftwjcroft Mount Shasta, CA
    edited April 2023

    re: "railing percentage" changing over time.

    The GUI developer, Richard @retiutut may have further insights. Actually I think this is happening in the ADS1299 hardware. As it will, over time, try to normalize whatever is happening at the electrodes. Remember, no electrode skin contact is a completely unacceptable situation as the ADS1299 is then at the mercy of whatever EMF environmental noise is present in your room. Both the electrode and connecting wires then act like 'antennas' for impinging EMF.

    re: combination of accelerometer and impedance

    Yes this is why I suggest the accelerometer is your primary indicator, and you only need to check the impedance if the subject is very still and accel is showing 0,0,0.

    re: deriving impedance values from Brainflow

    There are some other threads here on the Forum which suggest solutions for that. Use the Google Advanced Search button in the upper right to find those threads.

    William

  • Thanks a lot William! It's greatly appreciated!

Sign In or Register to comment.