An end-to-end AutoML framework for surface-EMG: filtering, event detection, feature extraction, model selection across gradient boosting, random forests and linear models, and deployable C++/Arduino code for microcontrollers such as the ESP32.

  • EMG
  • AutoML
  • Embedded
  • ESP32
pip install emg2mcu

emg2mcu 0.1.0 on PyPI · Python 3.10+ · MIT license

EMG2MCU's pipeline on the quickstart example: a deterministic synthetic three-channel recording with three gesture classes. From top to bottom: raw signal, bandpass + notch filtered signal, the detected events after onset detection, the time-domain features per window, and the model ranking across gradient boosting, random forests and linear models. Hover for values, drag to zoom, double-click to reset · open on its own.

How it works

  1. EMG preprocessingbandpass + notch filters, causal or zero-phase
  2. Event detectiononset / offset with envelope smoothing
  3. Windowingsliding windows with configurable overlap
  4. Feature extractiontime-domain (RMS, MAV, …) per channel per window
  5. Model selectionLightGBM, XGBoost, CatBoost, RF, SVM, MLP, …
  6. MCU deployexportable C++/Arduino, ESP32 biquad filter

EMG2MCU wires the whole loop with configuration dataclasses (DataConfig, PreprocessingConfig, EventsConfig, WindowingConfig, FeatureConfig, NormalizeConfig, ClassificationConfig) and a single automl() entry point. Filtering is available in zero-phase (benchmark) and causal-SOS (deploy) modes so the same parameters carry over to the on-device filter. The deploy step generates the biquad coefficients the firmware needs and scaffolds the C++/Arduino code that runs them on the chip.

What you get

Filtered signals

Zero-phase bandpass + notch filtering, ready to use as features or to drive the deployable causal filter with the same parameters.

Detected events

Onset/offset events from an envelope smoother, with a configurable sustain window to avoid spurious triggers between repetitions.

Windowed features

Per-channel, per-window time-domain features (RMS, MAV, ZC, SSC, …) optionally normalised before classification.

Model ranking

Cross-validated accuracy, F1 and ROC AUC for every algorithm in ClassificationConfig, sorted by the metric you choose.

The figures on this page are produced by running the pipeline in EMG2MCU and rendering the results with Plotly. Open the Sandbox to run the same example in your browser.

Get started

1 Install the package

pip install emg2mcu

Pulls in NumPy, pandas, SciPy, scikit-learn, LightGBM, XGBoost, CatBoost, joblib and tsfresh.

2 Build the docs locally (optional)

pip install "emg2mcu[docs]"

Adds Sphinx, pydata-sphinx-theme, autoapi, myst-parser, copybutton and sphinx-design for the Read the Docs site.

3 Run the example

from pathlib import Path
import tempfile
import numpy as np

import EMG2MCU

# 1. Build a deterministic synthetic three-class recording (no real data needed)
fs = 1000
rng = np.random.default_rng(42)
signal_blocks, label_blocks = [], []

def add_segment(label, duration_s, amplitudes):
    n = int(duration_s * fs)
    t = np.arange(n) / fs
    seg = rng.normal(0, 0.015, size=(3, n))
    if label >= 0:
        fade = np.minimum(1, np.minimum(t / 0.15, (duration_s - t) / 0.15))
        freqs = np.array([83, 117, 151]) + 7 * label
        carriers = np.sin(2 * np.pi * freqs[:, None] * t)
        seg += np.asarray(amplitudes)[:, None] * fade * carriers
    signal_blocks.append(seg.T)
    label_blocks.append(np.full(n, label, dtype=int))

for label, amps in enumerate([[0.8, 0.15, 0.15], [0.15, 0.8, 0.15], [0.15, 0.15, 0.8]]):
    add_segment(label, 3.0, amps)
    add_segment(-1, 0.5, [0, 0, 0])

signals = np.concatenate(signal_blocks, axis=0)
labels = np.concatenate(label_blocks)

# 2. Configure and run the AutoML pipeline
state = EMG2MCU.automl(
    data_cfg=EMG2MCU.DataConfig(data_file="synthetic.txt", label_names=["A", "B", "C"]),
    preprocessing_cfg=EMG2MCU.PreprocessingConfig(fs=fs, lowcut=30, highcut=450),
    feature_cfg=EMG2MCU.FeatureConfig(features=["RMS"]),
    output_dir=Path("output_emg2mcu"),
)

# 3. The ranked classifiers live in state["deploy"]["td_results"]
print(state["deploy"]["td_results"].sort_values("accuracy", ascending=False).head())

The full example notebook writes the synthetic recording to a temporary file, configures every stage and runs the full pipeline end-to-end. Generated artifacts land in the ignored output_emg2mcu/ directory.

About

EMG2MCU is developed by the LENeR-UEL lab at the State University of Londrina (UEL). It is open source under the MIT license; contributions, bug reports and feature requests are welcome on GitHub.