树莓派Pico通过ADC录制驻极体麦克风音频仅获白噪声问题排查
树莓派Pico ADC录制音频问题排查与解决
一、前置放大器搭建与测试
基于MCP6001运算放大器搭建驻极体麦克风前置电路,测试结果:
- 两节AA电池供电:电源电压
V_DD = 2.620(1)V,输出偏压V_out0 = 1.310(1)V,吹口哨时峰峰值信号V_out ~ 0.2-0.3V,信噪比良好; - Pico的
ADC_VREF (GPIO 35)和AGND (GPIO 31)供电:电源电压V_DD = 3.2(1)V,输出偏压V_0 = 1.58(10)V,稳定性下降,但信噪比满足录音需求。
二、Pico ADC采样验证
将前置放大器输出连接至ADC0 (GPIO 26),在Thonny MicroPython Shell中测试采样:
>>> from machine import Pin, ADC >>> mic = ADC(Pin(26)) >>> mic.read_u16() 32551 >>> mic.read_u16() 31911 >>> mic.read_u16() 31815
16位ADC最大值为2^16=65536,读数处于合理区间,说明ADC硬件连接正常。
三、原始录音代码的问题
编写的WAV录制代码生成的文件仅含白噪声,原代码如下:
from machine import Pin import time micPin = machine.ADC(Pin(26)) FILE_NAME = 'test17.wav' sampleRate = 44100 # 44.1 kHz - CD quality bitsPerSample = 16 num_channels = 1 # 1 - MONO, 2 - STEREO duration = 3 # in seconds bufferSize = duration * sampleRate # 1/s * s num_samples = bufferSize def create_wav_header(sampleRate, bitsPerSample, num_channels, num_samples): datasize = num_samples * num_channels * bitsPerSample // 8 o = bytes("RIFF", "ascii") # 1-4 (4byte) Marks file as RIFF o += (datasize + 36).to_bytes( 4, "little" ) # 5-8 (4byte) File size in bytes excluding this and RIFF marker o += bytes("WAVE", "ascii") # 9-12 (4bytes) File type o += bytes("fmt ", "ascii") # 13-16 (4bytes) Format Chunk Marker, trailing 0 o += (16).to_bytes(4, "little") # 17-20 (4bytes) Length of above format data o += (1).to_bytes(2, "little") # 21-22 (2bytes) Format type (1 - PCM) o += (num_channels).to_bytes(2, "little") # 23-24 (2bytes) o += (sampleRate).to_bytes(4, "little") # 25-28 (4bytes) o += (sampleRate * num_channels * bitsPerSample // 8).to_bytes(4, "little") # 29-32 (4bytes) o += (num_channels * bitsPerSample // 8).to_bytes(2, "little") # 33-34 (2bytes) o += (bitsPerSample).to_bytes(2, "little") # 35-36 (2bytes) o += bytes("data", "ascii") # 37-40 (4bytes) Data Chunk Marker o += (datasize).to_bytes(4, "little") # 41-44 (4bytes) Data size in bytes return o try: with open('/'+FILE_NAME, 'wb') as recFile: wav_header = create_wav_header( sampleRate, bitsPerSample, num_channels, num_samples ) recFile.write(wav_header) with open('/'+FILE_NAME, 'ab') as recFile: buffer = bytearray(bufferSize) for i in range(bufferSize): buffer[i] = micPin.read_u16() # print(buffer[i]) time.sleep(1/sampleRate) recFile.write(buffer) except (KeyboardInterrupt, Exception) as e: print("caught exception {} {}".format(type(e).__name__, e))
问题根源:micPin.read_u16()返回16位无符号整数,但bytearray的每个元素仅能存储8位数据,赋值时会自动截断高8位,只保留低8位,导致音频数据完全失真,表现为白噪声。取消注释print(buffer[i])后输出值远低于正常ADC读数,也验证了高位数据丢失的问题。
四、修复后的代码验证
修改代码,将16位ADC值通过amplitude.to_bytes(2,'little')转换为两个字节后存入缓冲区,测试用正弦波可生成正常音频文件,修复后代码:
from machine import Pin, ADC import time import math # for sine wave micPin = ADC(Pin(26)) FILE_NAME = 'test_sine.wav' sampleRate = 44100 # 44.1 kHz - CD quality bitsPerSample = 16 num_channels = 1 # 1 - MONO, 2 - STEREO duration = 3 # in seconds recordSize = duration * sampleRate # 1/s * s bufferSize = 1000 num_samples = recordSize def create_wav_header(sampleRate, bitsPerSample, num_channels, num_samples): datasize = num_samples * num_channels * bitsPerSample // 8 o = bytes("RIFF", "ascii") # 1-4 (4byte) Marks file as RIFF o += (datasize + 36).to_bytes( 4, "little" ) # 5-8 (4byte) File size in bytes excluding this and RIFF marker o += bytes("WAVE", "ascii") # 9-12 (4bytes) File type o += bytes("fmt ", "ascii") # 13-16 (4bytes) Format Chunk Marker, trailing 0 o += (16).to_bytes(4, "little") # 17-20 (4bytes) Length of above format data o += (1).to_bytes(2, "little") # 21-22 (2bytes) Format type (1 - PCM) o += (num_channels).to_bytes(2, "little") # 23-24 (2bytes) o += (sampleRate).to_bytes(4, "little") # 25-28 (4bytes) o += (sampleRate * num_channels * bitsPerSample // 8).to_bytes(4, "little") # 29-32 (4bytes) o += (num_channels * bitsPerSample // 8).to_bytes(2, "little") # 33-34 (2bytes) o += (bitsPerSample).to_bytes(2, "little") # 35-36 (2bytes) o += bytes("data", "ascii") # 37-40 (4bytes) Data Chunk Marker o += (datasize).to_bytes(4, "little") # 41-44 (4bytes) Data size in bytes return o try: with open('/'+FILE_NAME, 'wb') as recFile: wav_header = create_wav_header( sampleRate, bitsPerSample, num_channels, num_samples ) recFile.write(wav_header) print('Header written') with open('/'+FILE_NAME, 'ab') as recFile: buffer = bytearray() # sine wave creation for i in range(recordSize): x = (2 * math.pi) / 1000 # devide by an arbitray number # to create steps y = math.sin(i * x) amplitude = math.floor((2**16-1//2) + ((2**16-1)//2)*y) if i%bufferSize==0: # write buffer to file and empty it recFile.write(buffer) # print('buffer written') buffer = bytearray() buffer.extend(amplitude.to_bytes(2,'little')) else: buffer.extend(amplitude.to_bytes(2,'little')) except (KeyboardInterrupt, Exception) as e: print("caught exception {} {}".format(type(e).__name__, e))
后续只需将正弦波生成部分替换为真实麦克风ADC读数的转换即可正常录音。
五、录音项目优化建议
- 采样率调整:44.1kHz的采样率对MicroPython运行环境压力较大,建议降低至22050Hz或16000Hz,提升采样稳定性,同时满足语音录音需求;
- 硬件供电优化:给前置放大器使用独立电源,避免Pico电源波动导致的偏压不稳定,进一步提升信噪比;
- 缓冲区优化:预分配固定大小的
bytearray(如bytearray(bufferSize*2)),避免频繁创建和清空缓冲区,提升写入效率; - 数据格式校准:WAV标准的16位PCM为有符号整数,需将Pico ADC的无符号读数(065535)转换为有符号值(-3276832767),即
adc_value - 32768,再转换为小端字节流,确保音频播放正常。
内容的提问来源于stack exchange,提问作者tinker.toadie
相关产品推荐
相关产品推荐

