Python 是什么导致我的超级采样模拟出现负偏差?
我正在开发一个小测试程序,看看向ADC添加噪声以获得过采样位是否可行。在我们开始之前,先来点理论。奈奎斯特的采样定理表明,一位分辨率的增加需要四个额外的样本,而一般来说,多n位需要多2^(n+1)个样本。我正在模拟一个完美的10位ADC,它单调地返回0..1023的值,并且对于0-2V的输入没有噪声 为了获得更多的位,有必要添加随机分布的噪声(它实际上不必是随机的,但它必须看起来是随机的,就像白噪声一样。)我遇到的问题是,虽然分辨率在增加,但实际读数会被一些小的负值抵消。这里是1伏输入的一个样本输出(参考电压为2伏,因此对于单调ADC,计数应该正好是一半): 事实上,无论我运行模拟多少次,我的最终结果总是约为0.9995,而不是1;最后一个值应该是134217728,而不是134152551,它大约是65771,或者大约是额外18位分辨率的1/4(巧合?我下降了4)。我怀疑我的PRNG在某种程度上有偏差,但我使用的是Python附带的默认Mersenne TwisterPython 是什么导致我的超级采样模拟出现负偏差?,python,algorithm,statistics,Python,Algorithm,Statistics,我正在开发一个小测试程序,看看向ADC添加噪声以获得过采样位是否可行。在我们开始之前,先来点理论。奈奎斯特的采样定理表明,一位分辨率的增加需要四个额外的样本,而一般来说,多n位需要多2^(n+1)个样本。我正在模拟一个完美的10位ADC,它单调地返回0..1023的值,并且对于0-2V的输入没有噪声 为了获得更多的位,有必要添加随机分布的噪声(它实际上不必是随机的,但它必须看起来是随机的,就像白噪声一样。)我遇到的问题是,虽然分辨率在增加,但实际读数会被一些小的负值抵消。这里是1伏输入的一个样本
#!/usr/bin/python
#
# Demonstrates how oversampling/supersampling with noise can be used
# to improve the resolution of an ADC reading.
#
# Public domain.
#
import random, sys
volts = 1.000
reference = 2.000
noise = 0.01
adc_res = 10
def get_rand_bit():
return random.choice([-1, 1])
def volts_with_noise():
if get_rand_bit() == 1:
return volts + (noise * random.random() * get_rand_bit())
else:
return volts
def sample_adc(v):
# Sample ADC with adc_res bits on given voltage.
frac = v / reference
frac = max(min(frac, 1.0), 0.0) # clip voltage
return int(frac * (2 ** adc_res))
def adc_do_no_noise_sample():
return sample_adc(volts)
def adc_do_noise_sample(extra_bits_wanted):
# The number of extra samples required to gain n bits (according to
# Nyquist) is 2^(n+1). So for 1 extra bit, we need to sample 4 times.
samples = 2 ** (extra_bits_wanted + 1)
print "Sampling ", samples, " times for ", extra_bits_wanted, " extra bits."
# Sample the number of times and add the totals.
total = 0
for i in range(samples):
if i % 100000 == 99999:
print float(i * 100) / samples
sys.stdout.flush()
total += sample_adc(volts_with_noise())
# Divide by two (to cancel out the +1 in 2^(n+1)) and return the integer part.
return int(total / 2)
def convert_integer_to_volts(val, num_bits):
# Get a fraction.
frac = float(val) / (2 ** num_bits)
# Multiply by the reference.
return frac * reference
if __name__ == '__main__':
# First test: we want a 10 bit sample.
_10_bits = adc_do_no_noise_sample()
# Next, create additional samples.
_11_bits = adc_do_noise_sample(1)
_12_bits = adc_do_noise_sample(2)
_13_bits = adc_do_noise_sample(3)
_14_bits = adc_do_noise_sample(4)
_15_bits = adc_do_noise_sample(5)
_16_bits = adc_do_noise_sample(6)
_17_bits = adc_do_noise_sample(7)
_18_bits = adc_do_noise_sample(8)
_24_bits = adc_do_noise_sample(14)
_28_bits = adc_do_noise_sample(18)
# Print results both as integers and voltages.
print "10 bits: ", _10_bits, " volts: ", convert_integer_to_volts(_10_bits, 10)
print "11 bits: ", _11_bits, " volts: ", convert_integer_to_volts(_11_bits, 11)
print "12 bits: ", _12_bits, " volts: ", convert_integer_to_volts(_12_bits, 12)
print "13 bits: ", _13_bits, " volts: ", convert_integer_to_volts(_13_bits, 13)
print "14 bits: ", _14_bits, " volts: ", convert_integer_to_volts(_14_bits, 14)
print "15 bits: ", _15_bits, " volts: ", convert_integer_to_volts(_15_bits, 15)
print "16 bits: ", _16_bits, " volts: ", convert_integer_to_volts(_16_bits, 16)
print "17 bits: ", _17_bits, " volts: ", convert_integer_to_volts(_17_bits, 17)
print "18 bits: ", _18_bits, " volts: ", convert_integer_to_volts(_18_bits, 18)
print "24 bits: ", _24_bits, " volts: ", convert_integer_to_volts(_24_bits, 24)
print "28 bits: ", _28_bits, " volts: ", convert_integer_to_volts(_28_bits, 28)
如果你能给我一些建议,我将不胜感激。我的计划是最终将其应用于低成本微控制器,以实现高分辨率ADC。速度将是相当重要的,所以我可能会使用LFSR来生成PRNG位,这不会是Mersenne捻线机的一半,但对于大多数用途来说应该足够好,希望足够好。听起来你的噪声源是有偏的。我不熟悉Mersenne Twister,但我确实知道LFSR伪随机噪声发生器总是有轻微的偏差。通过扩展LFSR的长度,可以使偏差任意变小,但它始终存在。我不熟悉这里的域,但如果使用python2,可能会遇到意外的整数除法。有一些方法可以解决这个问题
>>> 10 / 3
3
>>> 10 * 1.0 / 3
3.3333333333333335
>>> from __future__ import division
>>> 10 / 3
3.3333333333333335
>>> 10 // 3
3
在sample_adc(..)
中,您可能希望舍入而不是截断(系统地向负无穷大舍入),即:
return int(frac * (2 ** adc_res) + 0.5)
而不是
return int(frac * (2 ** adc_res))
那么与一个的偏差并不总是在同一侧:
10 bits: 512 volts: 1.0
11 bits: 1025 volts: 1.0009765625
12 bits: 2046 volts: 0.9990234375
13 bits: 4100 volts: 1.0009765625
14 bits: 8196 volts: 1.00048828125
15 bits: 16391 volts: 1.00042724609
16 bits: 32784 volts: 1.00048828125
17 bits: 65528 volts: 0.999877929688
18 bits: 131111 volts: 1.00029754639
24 bits: 8388594 volts: 0.99999833107
28 bits: 134216558 volts: 0.999991282821
虽然要检查偏差,例如调用
adc\u do\u noise\u sample(..)
例如10000次(对于每个分辨率),然后计算平均偏差和该平均值的不确定度(并检查它是否与零兼容)。我快速测试了随机生成器中100000个数字的平均值,它大约在-0.0001到+0.0001之间,所以没有太大的偏差(关于预测的内容)。问题似乎是对~0.9995的偏差非常一致。你能补充一下“ADC”的含义吗?我也有点不确定你的意图。“决心”对我来说意味着“最小的可辨别的细节”。如果您的详细信息低于分辨率,则您将丢失该信息。“添加噪波”如何才能将该信息添加回去?您使用的是“分辨率”的不同含义吗?@Svante-模数转换器。@Svante,电压源的分辨率可以认为是无限的。添加噪声会导致ADC上的计数跳变一位,从而提高分辨率。如果没有信息理论的背景很难解释(我真的没有,但它主要是从书籍和网站上收集的知识)。这并不是说它似乎消除了/增加了偏见,而是你的volts\u with_noise()
的实现在我看来有点滑稽:50%的样本将添加零噪声。那是故意的吗?
10 bits: 512 volts: 1.0
11 bits: 1025 volts: 1.0009765625
12 bits: 2046 volts: 0.9990234375
13 bits: 4100 volts: 1.0009765625
14 bits: 8196 volts: 1.00048828125
15 bits: 16391 volts: 1.00042724609
16 bits: 32784 volts: 1.00048828125
17 bits: 65528 volts: 0.999877929688
18 bits: 131111 volts: 1.00029754639
24 bits: 8388594 volts: 0.99999833107
28 bits: 134216558 volts: 0.999991282821