突破采样率极限:Arduino 上利用欠采样技术测量高频信号的原理与误差分析
👁 7 阅读 · 2026-08-27 · 嵌入式
在嵌入式开发中,ADC 采样率往往限制了对高频信号的测量。欠采样(Undersampling)技术利用频谱混叠原理,让低于奈奎斯特频率的采样率也能捕获高频信号信息。本文深入剖析欠采样在 Arduino 平台上的实现原理,结合代码示例展示如何测量高于采样率的高频信号,并详细分析量化误差、孔径抖动和混叠干扰等关键误差源,帮助开发者理解其适用边界与工程权衡。
# 引言
在嵌入式系统中,ADC 的采样率是硬性瓶颈。例如,Arduino Uno 的 10 位 ADC 最高采样率约 10 kSPS(实际受转换时间限制),而常见的高频信号(如 100 kHz 正弦波)远超此范围。传统思路是使用高速 ADC 或外置采样保持电路,但成本高、复杂度大。欠采样技术提供了一种巧妙的替代方案:利用频谱混叠,让低频采样率“映射”高频信号到低频区间,从而恢复其幅度和频率信息。本文面向有一定基础的嵌入式开发者,深入探讨其原理、实现步骤及误差控制。
# 欠采样原理
## 奈奎斯特采样定理与混叠
奈奎斯特定理要求采样率 fs 至少为信号最高频率 fmax 的两倍,否则发生混叠(Aliasing)。混叠是指高频分量折叠到低频区间,造成失真。然而,欠采样故意利用这一现象:当信号频率 f 满足 f = n*fs ± f_aliased(n 为整数,f_aliased 为混叠后的低频频率),且 f_aliased < fs/2 时,采样后的信号在频域表现为 f_aliased。通过测量 f_aliased,可以反推原始频率 f,前提是已知 n 或通过多次采样确定。
## 欠采样的数学表达
设采样周期 Ts=1/fs,采样序列 x[n] = A*sin(2πf*n*Ts + φ)。由于 f*Ts 可能大于 0.5,实际离散频率为 (f mod fs) 的等效值。例如,fs=10 kHz,f=105 kHz,则 f mod fs = 5 kHz,且 5 kHz < fs/2,因此采样后得到 5 kHz 信号。若 f=115 kHz,则 f mod fs = 15 kHz,但 15 kHz > fs/2,实际混叠频率为 fs - 15 kHz = 5 kHz(因为正弦波对称性)。因此,欠采样后频率 f_aliased = |f - n*fs|,且取最小值。
## 关键条件
- 信号必须是窄带的(带宽远小于 fs/2),否则多个频率分量会混叠重叠,无法分辨。
- 需要已知信号的大致频率范围,以确定 n 值。
- 采样时钟必须稳定,否则引入孔径抖动。
# Arduino 实现欠采样
## 硬件配置
Arduino 的 ADC 是逐次逼近型,转换时间约 100 μs(10 位分辨率),实际采样率受限于循环和转换时间。为了获得稳定采样率,建议使用定时器触发 ADC,并直接读取 ADC 数据寄存器,避免使用 analogRead() 的额外开销。
## 配置步骤
1. **设置 ADC 时钟**:Arduino 默认 ADC 时钟为 16 MHz 分频,可调整为 128 分频(125 kHz)或更低,但采样率受限于转换周期(13 个 ADC 时钟)。
2. **使用定时器触发**:例如,使用 Timer1 产生固定频率的 PWM 或比较匹配事件,触发 ADC 转换。
3. **配置 ADC 为自由运行模式或单次转换**:在中断中读取结果。
4. **计算实际采样率**:通过定时器配置精确计算。
## 代码示例
以下代码使用 Timer1 触发 ADC,采样率设为 10 kHz,测量 105 kHz 信号(预期混叠为 5 kHz)。
```c
// Arduino Uno (ATmega328P) 欠采样示例
// 使用 Timer1 触发 ADC,采样率 10 kHz
volatile uint16_t adc_value;
volatile bool data_ready = false;
void setup() {
Serial.begin(115200);
// 配置 ADC:参考电压 AVCC,通道 A0,右对齐
ADMUX = (1 << REFS0); // AVCC as reference
ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
// 使能 ADC,中断,预分频 128 -> ADC时钟 125 kHz
// 配置 Timer1 为 CTC 模式,比较匹配触发 ADC
TCCR1A = 0;
TCCR1B = (1 << WGM12) | (1 << CS11); // CTC, 8分频
OCR1A = 199; // 16 MHz / (8 * (199+1)) = 10 kHz
TIMSK1 = (1 << OCIE1A); // 使能比较匹配中断
// 设置 ADC 触发源为 Timer1 比较匹配
ADCSRB = (1 << ADTS2) | (1 << ADTS1) | (1 << ADTS0); // 触发源选择
ADCSRA |= (1 << ADATE); // 使能自动触发
sei(); // 全局中断
}
ISR(TIMER1_COMPA_vect) {
// 触发 ADC 转换(自动触发,无需额外代码)
}
ISR(ADC_vect) {
adc_value = ADC; // 读取结果
data_ready = true;
}
void loop() {
if (data_ready) {
data_ready = false;
// 输出采样值,用于串口绘图或分析
Serial.println(adc_value);
}
}
```
## 信号处理与频率估计
采集 N 个样本后,可通过过零检测或 FFT 计算混叠频率。例如,对 1000 个样本做 FFT,峰值频率即为 f_aliased。若已知 f 的大致范围,可计算 n = round(f / fs),然后原始频率 f = n*fs ± f_aliased(符号需根据 f mod fs 是否大于 fs/2 判断)。
# 误差分析
## 量化误差
ADC 分辨率为 10 位,量化步长 Vref/1024。对于正弦波,量化噪声功率约为 q^2/12,信噪比 SNR = 6.02N + 1.76 dB(N=10 时约 61.9 dB)。欠采样不改变量化误差,但混叠信号幅度可能较小,导致相对误差增大。
## 孔径抖动(Aperture Jitter)
采样时刻的不确定性(由时钟抖动和触发延迟引起)导致采样值误差。对于高频信号,抖动影响显著:误差幅度 ≈ 2πf * A * Δt。例如,f=100 kHz,A=1V,Δt=1 ns,误差约 0.63 mV,可能小于量化步长,但若 f 更高或抖动更大,误差会超过量化噪声。Arduino 的定时器中断抖动可能达几微秒,因此不适合极高频率。
## 混叠干扰
若信号含有多个频率分量,欠采样会导致不同频率混叠到同一低频,无法分离。例如,105 kHz 和 115 kHz 在 fs=10 kHz 时都混叠到 5 kHz。因此,必须使用带通滤波器预滤波,确保信号带宽小于 fs/2。
## 时钟精度
Arduino 的晶振精度约 ±20 ppm,但定时器分频误差可能更大。采样率误差会导致频率估计偏差。例如,fs 实际为 9.99 kHz,则 105 kHz 混叠频率为 |105 - 10*9.99| = 5.1 kHz,而非 5 kHz。
# 注意事项
- **信号幅度**:欠采样要求信号幅度在 ADC 输入范围内,且最好接近满量程以利用分辨率。
- **抗混叠滤波**:硬件上必须加低通滤波器,截止频率低于 fs/2,否则无法避免混叠重叠。
- **确定 n 值**:需要预先知道信号频率的大致范围,否则无法唯一确定原始频率。
- **实时性**:欠采样适合周期性信号,不适合瞬态或非周期信号。
- **替代方案**:若频率极高(>1 MHz),Arduino 的定时器抖动和 ADC 带宽不足,建议使用专用高速 ADC 或比较器。
# 总结
欠采样技术为嵌入式开发者提供了一种低成本测量高频信号的途径,尤其适用于窄带周期信号。通过理解混叠原理、合理配置定时器和 ADC,并严格控制误差源,可以在 Arduino 上实现有效测量。但需牢记其局限性:带宽限制、频率模糊和抖动影响。在实际项目中,应结合硬件滤波和软件算法,权衡精度与复杂度。