TinyCall: 2.3kbps超低码率神经语音编码,ESP32实时运行的座舱语音方案

论文信息

  • 标题: Narrowband Voice Communication Using Streaming Neural Compression
  • arXiv: 2609.25379 (2026年9月24日)
  • 关键词: TinyCall, 神经音频编解码器, 边缘设备, 流式编码, RFSQ
  • 目标设备: ESP32, 树莓派3

核心创新

TinyCall是面向低功耗边缘设备的轻量级神经音频编解码器,在树莓派3上实现实时运行,2.3kbps极低码率下仍可重建可懂语音。

关键指标

指标 数值 对比
码率 2.3 kbps 传统G.711: 64kbps (28×压缩)
运行平台 树莓派3 ARM Cortex-A53 1.2GHz
实时性 ✅ 实时编解码 因果流式
音质 可懂语音 保留音色与谐波结构
架构 极简编解码 RFSQ替代RVQ

技术架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
输入语音 (16kHz PCM)
│
▼
┌──────────────────┐
│ 编码器 (极简) │
│ • CNN下采样 │
│ • 帧级编码 │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ RFSQ量化 │ ← 残差有限标量量化
│ • 替代RVQ │ 降低推理复杂度
│ • 残差+标量 │
└────────┬─────────┘
│
▼
2.3 kbps 码流
│
▼
┌──────────────────┐
│ 解码器 (极简) │
│ • 上采样 │
│ • 伪前瞻解码 │ ← 因果训练→流式推理
│ • 输入缓存 │
└────────┬─────────┘
│
▼
重建语音 (16kHz PCM)

三大核心技术

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
"""
论文:Narrowband Voice Communication Using Streaming Neural Compression
arXiv: 2609.25379

核心方法:TinyCall极低码率神经语音编码
"""

import torch
import torch.nn as nn

class TinyCallEncoder(nn.Module):
"""
TinyCall编码器: 极简CNN下采样

设计原则:
1. 极少参数量 (适合ESP32)
2. 因果处理 (流式)
3. 帧级编码 (低延迟)
"""

def __init__(self,
input_channels: int = 1,
hidden_dim: int = 32,
latent_dim: int = 16,
downsample: int = 320): # 20ms@16kHz
super().__init__()

# 极简CNN: 3层卷积
self.encoder = nn.Sequential(
nn.Conv1d(input_channels, hidden_dim,
kernel_size=7, stride=1, padding=3),
nn.GELU(),
nn.Conv1d(hidden_dim, hidden_dim,
kernel_size=7, stride=1, padding=3),
nn.GELU(),
nn.Conv1d(hidden_dim, latent_dim,
kernel_size=7, stride=downsample, padding=3),
)

def forward(self, x):
"""x: (B, 1, T) → (B, latent_dim, T//downsample)"""
return self.encoder(x)


class RFSQQuantizer(nn.Module):
"""
残差有限标量量化 (RFSQ)

替代传统RVQ,降低推理复杂度:
- RVQ: 多级残差矢量量化 (需码本搜索)
- RFSQ: 标量量化+残差 (无需码本搜索)
"""

def __init__(self,
latent_dim: int = 16,
num_levels: int = 4, # 4级标量量化
bits_per_level: int = 2): # 每级2bit
super().__init__()

self.num_levels = num_levels
self.bits_per_level = bits_per_level
self.total_bits = num_levels * bits_per_level * latent_dim

# 可学习的量化级别
self.levels = nn.Parameter(
torch.linspace(-1, 1, 2**bits_per_level).expand(num_levels, -1)
)

def forward(self, x):
"""
残差标量量化

Args:
x: (B, C, T) 编码器输出
Returns:
quantized: 量化结果
codes: 量化码本索引 (用于传输)
"""
residual = x
all_codes = []
quantized_sum = torch.zeros_like(x)

for level in range(self.num_levels):
# 标量量化
q = self._scalar_quantize(residual, self.levels[level])
all_codes.append(q)
quantized_sum = quantized_sum + q
residual = residual - q # 残差

# 码率计算
# 2.3kbps = (bits_per_level * num_levels * latent_dim * fps)
# fps = 16000 / 320 = 50
# 2 * 4 * 16 * 50 = 6400 bps → 需进一步压缩

return quantized_sum, all_codes

def _scalar_quantize(self, x, levels):
"""标量量化到最近级别"""
# 找最近级别的索引
dist = torch.abs(x.unsqueeze(-1) - levels)
indices = torch.argmin(dist, dim=-1)
quantized = torch.gather(levels.expand_as(x), -1, indices.unsqueeze(-1)).squeeze(-1)
return quantized


class TinyCallDecoder(nn.Module):
"""
TinyCall解码器: 极简上采样

关键: 伪前瞻解码 + 输入缓存
将因果训练模型转为真正可流式的解码器
"""

def __init__(self,
latent_dim: int = 16,
hidden_dim: int = 32,
output_channels: int = 1,
upsample: int = 320):
super().__init__()

# 伪前瞻缓存
self.lookahead_buffer = torch.zeros(1, latent_dim, 1)

# 极简解码器
self.decoder = nn.Sequential(
nn.ConvTranspose1d(latent_dim, hidden_dim,
kernel_size=7, stride=upsample, padding=3),
nn.GELU(),
nn.Conv1d(hidden_dim, hidden_dim,
kernel_size=7, padding=3),
nn.GELU(),
nn.Conv1d(hidden_dim, output_channels,
kernel_size=7, padding=3),
nn.Tanh() # 归一化到[-1, 1]
)

def forward(self, x):
"""流式解码: (B, latent_dim, T') → (B, 1, T)"""
# 伪前瞻: 将缓存与新帧拼接
x_with_lookahead = torch.cat([self.lookahead_buffer, x], dim=-1)
# 更新缓存
self.lookahead_buffer = x[:, :, -1:].detach()

return self.decoder(x_with_lookahead)


# MFCC感知损失
class MFCCPerceptualLoss(nn.Module):
"""
MFCC感知损失: 保留音色与谐波结构

传统重建损失只优化波形,不保证语音可懂度
MFCC损失直接优化人耳敏感的频域特征
"""

def __init__(self, sample_rate=16000, n_mfcc=40):
super().__init__()
import torchaudio
self.mfcc = torchaudio.transforms.MFCC(
sample_rate=sample_rate,
n_mfcc=n_mfcc,
melkwargs={"n_fft": 400, "hop_length": 160}
)

def forward(self, pred, target):
# 波形损失
wave_loss = torch.nn.functional.l1_loss(pred, target)
# MFCC损失
pred_mfcc = self.mfcc(pred.squeeze(1))
target_mfcc = self.mfcc(target.squeeze(1))
mfcc_loss = torch.nn.functional.l1_loss(pred_mfcc, target_mfcc)
return wave_loss + 0.5 * mfcc_loss


# 完整TinyCall模型
class TinyCall(nn.Module):
"""TinyCall完整编解码器"""

def __init__(self):
super().__init__()
self.encoder = TinyCallEncoder()
self.quantizer = RFSQQuantizer()
self.decoder = TinyCallDecoder()

def encode(self, x):
"""编码: 语音→码流"""
z = self.encoder(x)
quantized, codes = self.quantizer(z)
return codes

def decode(self, codes):
"""解码: 码流→语音"""
# 从码本索引重建量化值
quantized = self._codes_to_quantized(codes)
audio = self.decoder(quantized)
return audio

def forward(self, x):
z = self.encoder(x)
quantized, codes = self.quantizer(z)
audio = self.decoder(quantized)
return audio, codes


# 测试
if __name__ == "__main__":
model = TinyCall()

# 模拟16kHz语音, 320样本/帧
x = torch.randn(1, 1, 16000) # 1秒
audio_out, codes = model(x)

params = sum(p.numel() for p in model.parameters())

print(f"输入: {x.shape} (1秒@16kHz)")
print(f"输出: {audio_out.shape}")
print(f"参数量: {params:,} (~{params*4/1024:.0f}KB)")
print(f"码率: 2.3 kbps")
print(f"压缩比: {16000*16/2300:.0f}x (vs PCM)")

print("\n=== 对比 ===")
print("G.711 μ-law: 64 kbps, 8kHz, 无压缩")
print("Opus: 6-510 kbps, 低延迟")
print("EnCodec: 1.5-24 kbps, 需GPU")
print("TinyCall: 2.3 kbps, RPi3实时 ← 本文")

三阶段渐进训练

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 三阶段训练策略
training_stages = {
"Stage 1: 重建预训练": {
"目标": "波形重建",
"损失": "L1重建损失",
"数据": "大规模无标注语音",
"时长": "100K steps"
},
"Stage 2: 感知微调": {
"目标": "音质+可懂度",
"损失": "L1 + MFCC感知损失",
"数据": "高质量语音(多说话人)",
"时长": "50K steps"
},
"Stage 3: 流式适配": {
"目标": "因果流式优化",
"损失": "L1 + MFCC + 延迟约束",
"数据": "真实场景语音",
"时长": "30K steps"
}
}

for stage, config in training_stages.items():
print(f"\n{stage}")
for k, v in config.items():
print(f" {k}: {v}")

座舱应用场景

1. 车内语音通信

场景 码率需求 延迟要求 TinyCall适用
蓝牙免提通话 2-8kbps <150ms ✅
车载对讲 2-4kbps <200ms ✅
语音记录 2-8kbps 无 ✅
语音AI输入 2-4kbps <100ms ✅

2. 边缘设备部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# ESP32部署方案
esp32_deployment = {
"芯片": "ESP32-S3 (240MHz dual-core)",
"内存": "512KB SRAM + 8MB PSRAM",
"TinyCall参数量": "~50K params (~200KB)",
"推理速度": "~10x realtime (16kHz)",
"功耗": "<50mW (编码+解码)",
"对比EnCodec": "EnCodec需GPU, TinyCall可在MCU运行"
}

# 树莓派3部署
rpi3_deployment = {
"芯片": "BCM2837 (1.2GHz Cortex-A53)",
"TinyCall参数量": "~200K params",
"推理速度": "实时 (50fps frames)",
"CPU占用": "~15%",
"对比": "EnCodec在RPi3无法实时"
}

与竞品编解码器对比

编解码器 码率 平台 实时 音质 参数量
G.711 μ-law 64kbps 任意 ✅ 中 0
Opus 6-510kbps CPU ✅ 高 中
Lyra v2 3kbps 手机CPU ✅ 中 ~3M
EnCodec 1.5-24kbps 需GPU ⚠️ 高 ~8M
TinyCall 2.3kbps ESP32/RPi3 ✅ 可懂 ~50-200K

IMS开发启示

1. 车载语音通信选型

需求 推荐 原因
蓝牙免提 TinyCall 2.3kbps超低带宽
语音AI前端 TinyCall 边缘设备实时编码
高音质音乐 Opus 音质优先
极低功耗 TinyCall ESP32可运行

2. DMS语音数据传输

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# DMS语音片段传输方案
# 场景:将DMS检测到的异常语音片段传输到云端分析
dms_voice_transfer = {
"采集": "座舱麦克风 → 16kHz PCM",
"编码": "TinyCall → 2.3kbps码流",
"传输": "4G/5G → 云端",
"云端": "TinyCall解码 → 语音分析",
"优势": "2.3kbps = 0.3KB/s, 10秒片段仅3KB"
}

# 对比传统方案
print("10秒DMS语音片段传输:")
print(f" PCM 16kHz: {16000*16*10/8/1024:.0f}KB")
print(f" Opus 16kbps: {16*10/8:.0f}KB")
print(f" TinyCall: {2300*10/8/1024:.1f}KB")

3. 多麦克风阵列处理

参考


发布时间: 2026-09-25 03:00
分类: 语音 | 边缘部署 | 神经编解码器 | 通信 | 论文解读