Files
javis/docs/WAKE_WORD.md
T
2026-09-21 21:44:00 +02:00

5.8 KiB
Raw Blame History

Custom wake word ("Rufus")

Javis uses openWakeWord. The built-in models are English phrases (hey_jarvis, alexa, hey_mycroft, hey_rhasspy, timer, weather). Anything else — e.g. Rufus — is a small custom .onnx classifier that you train once on synthetic speech (no recordings of your own voice needed) and drop into Javis. Slaves download it from the master automatically.

1. How Javis loads wake-word models

wake_word.model in config.yaml accepts three forms:

Value Meaning
hey_jarvis built-in openWakeWord model
rufus custom model at <paths.base_dir>/models/wakeword/rufus.onnx (default ~/.javis/models/wakeword/rufus.onnx)
D:/models/rufus.onnx explicit path to a custom model

The detector keys predictions by the file stem, so the file must be named <name>.onnx. Slaves receive the bare name (rufus) from the master and fetch the file via GET /api/v1/wake_word/model on first use, caching it under <slave config dir>/models/rufus.onnx.

2. Train the model

Training runs on Linux (WSL2 on Windows works) or free Google Colab; it needs a GPU for the ~10 min synthetic-speech step but finishes in well under an hour.

Option A — Colab (no setup)

Open openWakeWord's automatic_model_training.ipynb in Colab, run the cells top to bottom, and change only the config cell:

config["target_phrase"] = ["rufus", "hey rufus", "roofus", "hey roofus"]
config["model_name"]    = "rufus"
config["n_samples"]     = 5000      # positives; more = better, 20k+ for production
config["n_samples_val"] = 1000
config["steps"]         = 20000

Download my_custom_model/rufus.onnx when it finishes.

Option B — locally (WSL2 / Linux, reproducible)

# one-time environment
python3 -m venv ~/oww && source ~/oww/bin/activate
git clone https://github.com/dscripka/openwakeword && pip install -e ./openwakeword
git clone https://github.com/rhasspy/piper-sample-generator
wget -O piper-sample-generator/models/en_US-libritts_r-medium.pt \
  https://github.com/rhasspy/piper-sample-generator/releases/download/v2.0.0/en_US-libritts_r-medium.pt
pip install piper-phonemize webrtcvad mutagen torchinfo torchmetrics speechbrain \
            audiomentations torch-audiomentations acoustics pronouncing datasets deep-phonemizer

# data: room impulse responses, background noise, pre-computed negative features
# (copy the "Download Data" cells from the notebook — ~3 GB total)

# config
cp openwakeword/examples/custom_model.yml rufus.yml
#   target_phrase: ["rufus", "hey rufus", "roofus", "hey roofus"]
#   model_name: rufus
#   n_samples: 5000  n_samples_val: 1000  steps: 20000
#   background_paths: ["./audioset_16k", "./fma"]
#   false_positive_validation_data_path: validation_set_features.npy
#   feature_data_files: {ACAV100M_sample: openwakeword_features_ACAV100M_2000_hrs_16bit.npy}

python openwakeword/openwakeword/train.py --training_config rufus.yml --generate_clips
python openwakeword/openwakeword/train.py --training_config rufus.yml --augment_clips
python openwakeword/openwakeword/train.py --training_config rufus.yml --train_model
# → my_custom_model/rufus.onnx

Tips that matter for a German household

  • Spell out the German pronunciation. The synthetic voices are English; add phonetic variants to target_phrase so the model hears the "u" you actually say: "roofus", "ruufus". Include both the bare name and "hey <name>".
  • Add adversarial negatives. custom_negative_phrases in the YAML (e.g. ["rufen", "ruf mal", "ruhe", "bus", "roof"]) sharply cuts false wakes on similar-sounding words.
  • Two-syllable names with a hard consonant ("Rufus", "Jarvis") work far better than short or vowel-heavy ones.
  • Short names false-trigger more: expect to run threshold: 0.6–0.7 rather than the default 0.5. Tune in Settings → Wake Word; the Status tab shows the live score.

3. Install in Javis

mkdir $env:USERPROFILE\.javis\models\wakeword
copy rufus.onnx $env:USERPROFILE\.javis\models\wakeword\

config.yaml:

wake_word:
  model: rufus
  threshold: 0.6

Restart Javis. The log shows openWakeWord loaded (…\rufus.onnx, key=rufus, …). Paired slaves pick up the new name on their next config poll (≤ 60 s), download the model from the master and reload the detector — nothing to copy by hand. For a slave that cannot reach the master at that moment, place the file at ~/.javis-slave/models/rufus.onnx (or %LOCALAPPDATA%\JavisSlave\models\).

4. Verify

  • Say the name at normal speaking distance: the wake chime plays and the Status tab's Wake score bar jumps. Watch for a minute of normal conversation without triggers; if it fires spuriously, raise the threshold or retrain with more negatives.

  • Quick offline check without Javis:

    python -c "from openwakeword.model import Model; m=Model(wakeword_models=[r'%USERPROFILE%\.javis\models\wakeword\rufus.onnx'], inference_framework='onnx'); print(m.predict_clip(r'test.wav'))"
    

Troubleshooting

Symptom Cause / fix
wake word model 'rufus' is neither a built-in … nor a custom .onnx File missing or misnamed — it must be rufus.onnx in models/wakeword/.
Slave says Wake-Word-Modell 'rufus' konnte nicht geladen werden Master unreachable during download or master itself has no custom file; check GET http://master:8765/api/v1/wake_word/model returns 200 with slave credentials.
Fires on TV / music Raise threshold; retrain with more background_paths hours and custom_negative_phrases.
Misses when spoken quickly / far away Add phrase variants, more n_samples (20k+), and set augmentation_rounds higher in the YAML.