campplus-speaker
Speaker-embedding (voiceprint) bundle for IB-Robot,
packaged as a schema-v3 inference_manifest.json.
One utterance in, one 192-dimensional L2-normalized vector out, so a cosine similarity is a plain dot product. IB-Robot's voice agent uses it to decide whether an utterance heard inside a wake-word session came from the person who opened that session.
Model
3D-Speaker CAM++ (speech_campplus_sv_zh-cn_16k-common), as exported to ONNX by
sherpa-onnx.
Upstream model card: https://www.modelscope.cn/models/iic/speech_campplus_sv_zh-cn_16k-common.
Apache-2.0. The ONNX graph is unmodified; this bundle adds the manifest and the
adapter description. See SOURCE.md for the artifact digest and how it was obtained.
Input contract
The model consumes features, not audio: 80-bin Kaldi log-mel filterbank,
16 kHz mono, 25 ms windows every 10 ms, Povey window, pre-emphasis 0.97,
snip_edges=false, then per-utterance mean subtraction (global-mean).
IB-Robot computes these on the host in
src/voice_asr_service/voice_asr_service/speaker_id/features.py, which is pinned
against sherpa-onnx's own extractor by test_speaker_features.py.
The host also splits an utterance into fixed 200-frame (2.0 s) windows, which is what the input shape above reflects. Shorter utterances are tiled from their own frames rather than zero-padded; longer ones become consecutive windows with the last aligned to the end, and the per-window unit vectors are averaged. This is part of the declared preprocessing contract, so every deployment applies it and vectors stay comparable across them.
| Tensor | Semantic | Shape | dtype |
|---|---|---|---|
| input | host.campplus.features |
(1, 200, 80) |
float32 |
| output | host.campplus.embedding |
(1, 192) |
float32 |
Deployments
| Name | Backend | Target |
|---|---|---|
torch_cpu |
onnx |
ONNX Runtime, CPU, 2 intra-op threads |
There is no Ascend OM deployment. Measured on CANN 8.1.RC1 / Ascend310P1
(2026-10-28): the fp32 graph does not compile (Conv2D has no fp32 AI-core
implementation, EZ3002), and both fp16 and mixed_float16 overflow β for
identical T=200 features, 4 of 9 clips returned 10xβ320x the reference norm
and the remainder agreed to only 0.95β0.98, against a decision margin of 0.60.
The variable-length input is a second obstacle: an OM needs a static shape, and
quantizing the length into gears coarse enough to ship costs more accuracy
(worst case 0.27 agreement) than the decision margin allows.
Usage
./scripts/download_models.py --models campplus-speaker
Then point a robot config at it:
voice_asr:
speaker_embedding:
enabled: true
bundle_path: models/campplus-speaker
deployment: torch_cpu
Privacy
A voiceprint is biometric data. IB-Robot keeps vectors in process memory and in the message they are published in; nothing is written to disk or kept across sessions. Deployments should keep the ROS domain isolated, since the topic is readable by any node that joins it.