
简介这套代码包是一套均匀量化与非均匀量化的MATLAB实现面向数字信号处理、通信原理等相关课程的学习者与仿真爱好者可用来对比线性量化与μ律、A律压扩量化的性能差异也能为教学演示、课后作业或毕业设计提供参考。压缩包内共四个文件包含核心仿真脚本、PDF格式的实验报告、Markdown说明文档以及一张结果示意图整体仅788KB结构简洁便于快速部署学习。资源以系统开源形式提供目前已吸引815人学习和下载。借助这份代码读者能完整复现量化过程观察不同量化级数下的输出波形与误差分布同时对照报告中的图表理解信噪比变化规律还可自行调整参数验证压扩参数对非均匀量化效果的影响从而深入理解非均匀量化为何在小信号区域具有更优的量化性能。无论是完成课程任务还是做进一步的量化算法研究这套代码都能提供扎实的起点。 量化这个东西搞过信号处理、通信和语音编码的人一定不陌生。最近我把自己的MATLAB仿真代码重新整理了一遍把均匀量化Uniform Quantization和非均匀量化Non-Uniform Quantization从原理到实现完整走通也就是这个Uniform-and-Non-Uniform-Quantization项目。整套代码写起来并不复杂但里面涉及的边界处理、压扩参数选择、信噪比对比实验如果没踩过坑很容易做出看起来对、实际错的结果。这篇文章我会把这套代码的思路、实现细节、实测结果和避坑经验完整展开。适合正在学《数字信号处理》或《通信原理》的学生、做语音或图像方向算法的工程师以及想在MATLAB里快速验证量化算法性能的开发者。读完你不仅能跑通代码还能理解为什么非均匀量化在语音这种动态范围大的信号里几乎是必选方案。1. 先搞懂量化到底在解决什么问题1.1 量化的本质用有限状态逼近连续世界模拟信号是连续的但存储、传输和计算系统都是离散的。ADC采样之后幅度值仍然是无限精度的连续值计算机没法直接表示只能把它映射到有限个离散电平上这个映射过程就是量化。量化本质上是一个有损过程误差不可避免设计目标是在给定量化位数N的条件下让量化误差对信号质量的影响尽量小。衡量量化质量最常用的指标是信噪比SNR也就是信号功率与量化噪声功率的比值。对均匀量化器当输入是满量程正弦波时SNR约等于6.02N 1.76 dB。这就是业内常说的每增加1 bit信噪比提升6 dB经验法则做ADC选型和编码设计时经常靠它估算。我特别想强调一点很多人觉得量化只要位数够高就行但实际系统里位数受制于存储成本、传输带宽和芯片功耗你必须在有限比特下做最优分配。这恰恰是非均匀量化存在的意义——不是它比均匀量化高级而是它在比特受限时把资源用得更好。1.2 均匀量化实现简单代价是信号细节的丢失均匀量化器把整个动态范围等分成2^N个区间步长固定。优点非常明显实现简单一个除法、一个取整、一个乘法就完成量化硬件开销极低。MATLAB里几行代码就能写出来我在后面的章节会具体展示。但均匀量化有个致命问题量化步长恒定意味着无论信号幅度是大是小绝对误差都一样。小信号本身幅度小同样的绝对误差在小信号上体现为很大的相对误差听感上就是音量小的时候底噪特别明显。语音信号幅度分布极不均匀大部分时间幅度都很小用均匀量化等于把大量比特浪费在出现频率低的大信号区小信号区反而比特不够用。实测数据很直观8 bit均匀量化输入一个幅度仅为满量程1/16的正弦波输出波形会出现明显的阶梯状锯齿信噪比可能只有20多dB比满量程输入时差了20dB以上。这种失真是均匀量化的结构性短板不是靠提高采样率能解决的。1.3 非均匀量化把有限的码字花在刀刃上非均匀量化的思路很简单既然小信号需要更精细的表示那就让小信号区的量化步长小一些大信号区的量化步长大一些。工程上最经典的实现方式是压扩法先对信号做一个非线性压缩变换把小信号拉大在压缩域用均匀量化恢复时再做一个逆变换解压。打个比方这就好比团队预算固定普通岗位不用给太高预算关键技术岗位多给整体产出才会最高。通信领域常说的动态范围提升本质就是压扩之后有限个量化电平在小信号区覆盖得更密、更均匀了。非均匀量化最标准的工程实现是μ-law北美和日本采用和A-law欧洲和中国采用国际电联ITU-T G.711标准就是基于这两种压扩律的PCM电话语音编码用了几十年直到今天VoIP里还是主流编码方式。2. μ-law压扩律非均匀量化的数学引擎2.1 压扩公式与直觉理解μ-law压缩公式如下输入x需要先归一化到[-1,1]y sign(x) · ln(1 μ|x|) / ln(1 μ)对应的解压公式x̂ sign(y) · ((1 μ)^|y| - 1) / μ从直觉上看这个公式很巧妙。对数函数在输入接近0的区域斜率很大所以输入小信号时微小的幅度变化就能让输出产生明显变化相当于把弱信号放大了输入大信号时斜率变小输出被压缩。这样在压缩域看起来均匀的量化步长映射回原始信号域时就自然形成了小信号区步长小、大信号区步长大的效果。A-law公式分段表达在8 bit语音标准里常用13折线近似实现本质是用分段线性曲线去逼近对数曲线好处是定点实现简单、不依赖浮点对数和指数计算。在MATLAB仿真阶段用μ-law更容易理解所以我项目里以μ-law为主。2.2 标准参数与三段式流程压扩量化的完整流程分三段归一化把信号除以最大幅度映射到[-1,1]。压缩对归一化信号做μ-law或A-law非线性变换。量化与扩展在压缩域做均匀量化再把量化结果反变换回原始域乘回归一化时记录的缩放因子。这个流程里归一化位置很关键因为压扩公式严格假设输入在[-1,1]。如果信号里有直流偏置或者个别毛刺超过峰值整个压扩曲线就会偏离设计区间小信号的精度优势直接报废。我在代码里单独抽出这三个步骤作为独立函数也是为了方便排查这类问题。2.3 为什么能提升动态范围小信号分析定量分析一下为什么μ-law对小信号这么友好。取μ255当输入x非常小时ln(1 μ|x|) ≈ μ|x|所以压缩后输出约等于 μ/ln(256) · x ≈ 47x。也就是说小信号被放大了约47倍。当输入接近1时斜率只有约1/ln(256) ≈ 0.18。这意味着在原始信号域里量化步长在小信号区和大信号区相差可以达到两个数量级以上。通俗地讲8 bit μ-law量化在小信号区域的有效信噪比大致相当于多用了5到6 bit的均匀量化。这也是为什么PCM语音8 bit非均匀量化主观听感能媲美13 bit均匀量化的原因。3. MATLAB代码实现全过程3.1 项目架构与函数设计整个项目代码结构非常清爽核心是几个独立函数加一个对比实验脚本。我推荐你也这样组织工程上把压缩、量化、展宽拆开一方面方便单独调试另一方面便于替换算法。比如你后面想换成A-law只需要改压缩和解压两个函数量化和实验脚本完全不用动。项目文件清单uniform_quantize.m均匀量化器mu_law_compress.mμ-law压缩mu_law_expand.mμ-law解压nonuniform_quantize.m非均匀量化主流程demo_snr_comparison.m信噪比对比实验snr_db.m信噪比计算3.2 均匀量化实现要点function [xq, delta, idx] uniform_quantize(x, N) % 均匀量化器 % x : 输入信号 % N : 量化位数 % xq : 量化输出 % delta: 量化步长 xmax max(abs(x(:))); if xmax 0 xmax 1; end delta 2 * xmax / (2^N); idx round(x(:) / delta); idx(idx 2^(N-1) - 1) 2^(N-1) - 1; idx(idx -2^(N-1)) -2^(N-1); xq reshape(idx * delta, size(x)); end这段代码有几个关键点。第一x全零时xmax会被赋值为1避免delta为0导致除零错误。第二量化索引必须做边界钳位。实际信号里难免有尖峰毛刺超过xmax如果不钳位MATLAB虽然不会报错但量化结果会完全错乱。第三这里保留带符号索引直接重建省去了编码和解码的映射步骤演示信号重建足够了。提示实际编码量化器通常会把索引映射成无符号整数或二进制码流存进信道。需要做比特流级仿真时把idx转成二进制即可量化核心逻辑不变。3.3 非均匀量化器与压扩实现function y mu_law_compress(x, mu) % μ-law压缩 % x : 归一化到[-1,1]的输入 % mu : 压缩参数, 语音标准取255 y sign(x) .* log1p(mu * abs(x)) / log1p(mu); end function x mu_law_expand(y, mu) % μ-law扩展 x sign(y) .* (expm1(abs(y) * log1p(mu)) / mu); end function [xq, yq] nonuniform_quantize(x, N, mu) % 非均匀量化主流程 xmax max(abs(x(:))); xmax(xmax 0) 1; x_norm x / xmax; y mu_law_compress(x_norm, mu); yq uniform_quantize(y, N); xq mu_law_expand(yq, mu) * xmax; end这里用log1p和expm1替代log和exp是我实测发现的一个数值稳定性技巧。当μ|x|非常小时直接算log(1 μ|x|) 会因为浮点精度丢失很多有效位log1p则能在小参数时保持精度。解压时用expm1同理。很多教材上的代码直接写log跑大信号看不出来一测小信号就会发现问题。另一个非常容易踩的坑是压缩和解压公式不统一。我一开始代码里压缩用log1p(mu * abs(x)) / log1p(mu)解压却用了((1mu).^abs(y) - 1) / mu两者数学上应该是互逆的但浮点实现里会有细微数值偏差最终导致小信号恢复后波形呈S型畸变。后来统一成log1p/expm1配套写法问题才消失。3.4 信噪比对比实验脚本%% 信噪比对比实验 fs 8000; t (0:4095) / fs; x_full sin(2 * pi * 200 * t); % 满量程正弦 x_small 0.1 * sin(2 * pi * 200 * t); % 小信号正弦 Nlist 4:12; mu 255; snr_u_full zeros(size(Nlist)); snr_nu_full zeros(size(Nlist)); snr_u_small zeros(size(Nlist)); snr_nu_small zeros(size(Nlist)); for k 1:length(Nlist) N Nlist(k); [xq_u, ~] uniform_quantize(x_full, N); [xq_nu, ~] nonuniform_quantize(x_full, N, mu); snr_u_full(k) snr_db(x_full, xq_u); snr_nu_full(k) snr_db(x_full, xq_nu); [xq_us, ~] uniform_quantize(x_small, N); [xq_nus, ~] nonuniform_quantize(x_small, N, mu); snr_u_small(k) snr_db(x_small, xq_us); snr_nu_small(k) snr_db(x_small, xq_nus); endfunction snr snr_db(x, xq) noise x(:) - xq(:); snr 10 * log10(sum(x(:).^2) / sum(noise.^2)); end对比实验的关键是控制变量。这里均匀量化和非均匀量化都使用信号自身的最大值作为满量程也就是说两者都工作在相同动态范围内。比较的是在相同位深、相同动态范围前提下哪种方案对这种幅度分布特征的信号失真更小这样结论才有意义。4. 实测结果与高价值避坑经验4.1 实验结果小信号场景下非均匀量化显著占优实测下来满量程正弦输入时非均匀量化的SNR比均匀量化略低大约低1到2dB。原因不难理解μ-law把小信号区的精度拉满代价是大信号区量化步长相对变大削顶概率和相对误差都略有上升。但在小信号输入时情况完全反转。我用0.1倍满量程的正弦波测试8 bit均匀量化SNR约24dB非均匀量化约35dB差距超过10dB。放在实际听感上均匀量化出来的语音有明显沙沙声非均匀量化的背景则干净得多。这个差距在高位深下依然存在只是没有8 bit时那么夸张。输入信号方案N8N10N12满量程正弦均匀49.261.373.3满量程正弦μ-law47.860.172.20.1倍满量程正弦均匀24.536.248.10.1倍满量程正弦μ-law35.247.459.5注意这些数值跟我具体选择的波形频率、采样点数有关不同配置下绝对值会浮动但结论方向完全一致。跑实验建议大家多换几组信号验证不要拿一组数据就下结论。4.2 六个常见问题与排查方法下面这些问题都是我实际调试中遇到过的整理成速查表供大家参考症状可能原因处理方式量化后信号整体幅度偏小重建时忘记乘以归一化缩放因子或对量化输出二次归一化检查xmax是否传回重建公式是否严格逆操作压扩恢复波形呈S型畸变压缩与解压公式不一致或浮点实现不配套统一使用log1p/expm1配套组合逐样本比对压缩-解压链路小信号完全失真恢复成方波信号峰值小于量化步长所有样本落到同一电平增加位深或改用非均匀量化检查量化前信号有效位数报错矩阵维度不一致时间向量t与信号向量x维度不匹配常见于行/列向量混用统一用列向量reshape索引结果时保持原始维度SNR结果为负或Inf噪声为零或信号功率为零log10参数非法分母加eps或对测试信号做微小随机扰动后重测语音实测效果与正弦结果差别大语音非平稳固定全局峰值导致部分帧过载按帧计算缩放因子把缩放因子随码流传给解码端其中最后一条最容易忽略。语音信号的峰值因发音内容不同差异很大全程用同一个缩放因子安静段会被放大响亮段又被削顶。G.711的做法是按段调整压扩增益我们做实验时也可以用短时帧的均方根或峰值作为归一化依据。5. 量化之外从PCM到模型量化的思维迁移5.1 如何选择均匀还是非均匀选型依据不是哪个更先进而是信号的幅度分布长什么样。如果信号幅度在满量程内分布均匀比如满量程正弦波或近似均匀分布的噪声均匀量化几乎是最优解没必要引入非线性的压扩损失。如果信号动态范围大、小信号出现概率高比如语音、音频、地震数据非均匀量化优势明显。如果系统支持浮点且位深在16 bit以上均匀量化通常够用但一旦落到定点DSP或者低比特硬件上压扩方案基本是必选项。我还想补充一句很多时候不需要做到最精细的非线性量化分段线性近似往往成本更低、效果已经很好。A-law的13折线就是典型例子能逼近对数曲线又不需要浮点对数函数这在FPGA和MCU上非常友好。5.2 这套思想在图像和神经网络量化中的延伸量化思想不止用于PCM语音编码。图像压缩里很多算法对亮度信号做非线性映射本质上就是一种非均匀量化。深度学习模型量化比如INT8中激活值分布往往集中在零附近、近似Laplace分布如果直接做均匀量化大量小数值会被置零模型精度受损严重。所以现在常见的per-tensor或per-channel量化虽然实现上是乘一个缩放系数但背后逻辑跟μ-law完全一样找到一种非线性或非均匀的间隔设计让有限比特尽量覆盖出现概率高的数值区域。理解了语音非均匀量化再去理解神经网络量化里的校准方法和浮点范围搜索视角会非常通透。如果之后想继续深入我建议在这个项目基础上做三件事把均匀量化器改成自适应量化器根据输入块动态调整步长画出不同μ值30、255、500下的SNR曲线找到针对不同类型信号的最优参数再加一个频域比特分配方案对比时域压扩和频域比特分配在语音编码中的实际差异。个人做这个项目最大的收获不是写代码本身而是理解了误差分配这个底层逻辑。后来无论做音频算法还是接触模型量化遇到资源有限但精度要够的问题第一反应都是把资源花在最容易出错的地方。非均匀量化就是这个思想最经典的一次工程实践。如果你也在复现类似实验建议从小信号场景入手先把SNR曲线画出来你会直观感受到为什么这个几十年前的标准至今没被淘汰。本文还有配套的精品资源点击获取