过采样的原理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了过采样的原理相关的知识,希望对你有一定的参考价值。

假定环境条件: 10位ADC最小分辨电压1LSB 为 1mv假定没有噪声引入的时候, ADC采样上的电压真实反映输入的电压, 那么小于1mv的话,如ADC在0.5mv时数据输出为0
我们现在用4倍过采样来, 提高1位的分辨率,
当我们引入较大幅值的白噪声: 1.2mv振幅(大于1LSB), 并在白噪声的不断变化的情况下, 多次采样, 那么我们得到的结果有 真实被测电压 白噪声叠加电压 叠加后电压 ADC输出 0.5mv 1.2mv 1.7mv 1mv 0.5mv 0.6mv 1.1mv 1mv 0.5mv -0.6mv -0.1mv 0mv 0.5mv -1.2mv -0.7mv 0mv ADC的和为2mv, 那么平均值为: 2mv/4=0.5mv!!! 0.5mv就是我们想要得到的
这里请留意, 我们平时做滤波的时候, 也是一样的操作喔! 那么为什么没有提高分辨率?是因为, 我们做滑动滤波的时候, 把有用的小数部分扔掉了, 因为超出了字长啊, 那么0.5取整后就是 0 了, 结果和没有过采样的时候一样是 0 ,
而过采样的方法时候是需要保留小数部分的, 所以用4个样本的值, 但最后除的不是4, 而是2! 那么就保留了部分小数部分, 而提高了分辨率!
从另一角度来说, 变相把ADC的结果放大了2倍(0.5*2=1mv), 并用更长的字长表示新的ADC值,
这时候, 1LSB(ADC输出的位0)就不是表示1mv了, 而是表示0.5mv, 而(ADC输出的位1)才是原来表示1mv的数据位,
下面来看看一下数据的变化:
ADC值相应位 9 8 7 6 5 4 3 2 1 0
0.5mv测量值0 0 0 0 0 0 0 0 0 0 0mv(10位ADC的分辨率1mv,小于1mv无法分辨,所以输出值为0)
叠加白噪声的4次过采样值的和 0 0 0 0 0 0 0 0 1 0 2mv
滑动平均滤波2mv/4次0 0 0 0 0 0 0 0 0 0 0mv(平均数, 对改善分辨率没作用)
过采样插值2mv/2 0 0 0 0 0 0 0 0 0 0 1 2mv/2=0.5mv, 将这个数作为11位ADC值, 那么代表就是0.5mv 这里我们提高了1位的ADC分辨率 。
这样说应该就很简单明白了吧, 其实多出来的位上的数据, 是通过统计输入量的分布, 计算出来的,
而不是硬件真正分辨率出来的, 引入噪声并大于1LSB, 目的就是要使微小的输入信号叠加到ADC能识别的程度(原ADC最小分辨率).
理论来说, 如果ADC速度够快, 可以无限提高ADC的分辨率, 这是概率和统计的结果
但是ADC的采样速度限制, 过采样令到最后能被采样的信号频率越来越低,
就拿stm32的ADC来说, 12ADC, 过采样带来的提高和局限
分辨率 采样次数 每秒采样次数
12ADC 1 1M
13ADC 4 250K
14ADC 16 62.5K
15ADC 64 15.6K
16ADC 256 3.9K
17DC 1024 976
18ADC 4096 244
19ADC 16384 61
20ADC 65536 15
要记住, 这些采样次数, 还未包括我们要做的滑动滤波

参考技术A 过采样是使用远大于奈奎斯特采样频率的频率对输入信号进行采样。设数字音频系统原来的采样频率为fs,通常为44.1kHz或48kHz。若将采样频率提高到R×fs,R称为过采样比率,并且R>1。在这种采样的数字信号中,由于量化比特数没有改变,故总的量化噪声功率也不变,但这时量化噪声的频谱分布发生了变化,即将原来均匀分布在0 ~ fs/2频带内的量化噪声分散到了0 ~ Rfs/2的频带上。右图表示的是过采样时的量化噪声功率谱。 若R>>1,则Rfs/2就远大于音频信号的最高频率fm,这使得量化噪声大部分分布在音频频带之外的高频区域,而分布在音频频带之内的量化噪声就会相应的减少,于是,通过低通滤波器滤掉fm以上的噪声分量,就可以提高系统的信噪比。这时,过采样系统的最大量化信噪比为公式如右图.
式中fm为音频信号的最高频率,R fs为过采样频率,n为量化比特数。
从上式可以看出,在过采样时,采样频率每提高一倍,则系统的信噪比提高3dB,换言之,相当于量化比特数增加了0.5个比特。由此可看出提高过采样比率可提高A/D转换器的精度。
但是单靠这种过采样方式来提高信噪比的效果并不明显,所以,还得结合噪声整形技术。

SofaTracer源码分析之日志采样原理

在SofaTracer中,有两种类型的日志采样,一是固定比率的采样,这也是默认的采样方式,二是自定义采样器。固定比率的采样只需要我们在项目的配置文件里面填上下列信息:

com.alipay.sofa.tracer.samplerName=PercentageBasedSamplercom.alipay.sofa.tracer.samplerPercentage=100

自定义采样器需要我们提供了一个Sampler接口的实现类,并重写sample方法,然后在配置文件里指定我们自定义的采样器。

com.alipay.sofa.tracer.samplerCustomRuleClassName = xxx.CustomOpenRulesSamplerRuler

1、Sampler接口

我们先来看看Sampler接口的类图:

里面最重要的一个方法就是sample(SofaTracerSpan sofaTracerSpan),方法返回SamplingStatus,这个SamplingStatus维护一个布尔类型的采样标记,和一些tags,在SofaTracer中,也只有唯一的一个实现类SofaTracerPercentageBasedSampler。

2、SofaTracerPercentageBasedSampler

SofaTacer中的日志采样是采用蓄水池算法获取一个BitSet。关于蓄水池算法和这个BitSet,后面我们专门说。

private final BitSet sampleDecisions; private final SamplerProperties configuration;
public SofaTracerPercentageBasedSampler(SamplerProperties configuration) { int outOf100 = (int) (configuration.getPercentage()); this.sampleDecisions = randomBitSet(100, outOf100, new Random()); this.configuration = configuration; }

在SofaTracerPercentageBasedSampler中:

@1:维护一个SamplerProperties,这个SamplerProperties里面的值就是我们在配置文件配置的采样比率和自定义的类的类名。

@2:维护一个BitSet,这个BitSet的大小是100,在构建这个BitSet时,要从所有日志中抓取的日志比例就是我们传入的百分比取整数,如果我们传的是100,那么就会全部采样,如果是20,那么就会采样20%.

2.1 SofaTracerPercentageBasedSampler#sample

public SamplingStatus sample(SofaTracerSpan sofaTracerSpan) { SamplingStatus samplingStatus = new SamplingStatus(); Map<String, Object> tags = new HashMap<String, Object>(); //@1 tags.put(SofaTracerConstant.SAMPLER_TYPE_TAG_KEY, TYPE); tags.put(SofaTracerConstant.SAMPLER_PARAM_TAG_KEY, configuration.getPercentage()); tags = Collections.unmodifiableMap(tags); samplingStatus.setTags(tags);
//@2 if (this.configuration.getPercentage() == 0) { samplingStatus.setSampled(false); return samplingStatus; } else if (this.configuration.getPercentage() == 100) { samplingStatus.setSampled(true); return samplingStatus; } //@3 boolean result = this.sampleDecisions.get((int) (this.counter.getAndIncrement() % 100)); samplingStatus.setSampled(result); return samplingStatus; }

@1:往SamplingStatus的tags属性里面填充相关信息,这里面主要放的是:

sampler.type--->PercentageBasedSampler

sampler.param--->我们配置的采样比率

@2:这是两个采样的极端,如果配置的比率为0,那么就不采样,如果配置的是100的话,那么就会全部采样,默认百分比为100

@3:根据蓄水池算法获取的BitSet,然后用一个原子整数变量自增后跟100取模,用这个结果作为下标从BitSet里面取值,如果为true,则采样。

2.2 SofaTracerPercentageBasedSampler#randomBitSet

public static BitSet randomBitSet(int size, int cardinality, Random rnd) { BitSet result = new BitSet(size); int[] chosen = new int[cardinality]; int i; //@1 for (i = 0; i < cardinality; ++i) { chosen[i] = i; result.set(i); } //@2 for (; i < size; ++i) { int j = rnd.nextInt(i + 1); if (j < cardinality) { result.clear(chosen[j]); result.set(i); chosen[j] = i; } } return result; }

这就是根据蓄水池算法获取到的Bitset,在stackoverflow上,可以看到实现。

@1:从下标0开始,直到(cardinality-1),设置BitSet

@2:  在BitSet中随机设置下标值,已达到分散的目的。

3、BitSet

BitSet的实现位于java.util包中。BitSet类实现了一个按需增长的位向量。BitSet的每一个组件都有一个boolean值。用非负的整数将BitSet的位编入索引。可以对每个编入索引的位进行测试、设置或者清除。通过逻辑与、逻辑或和逻辑异或操作,可以使用一个 BitSet修改另一个 BitSet的内容。默认情况下,set 中所有位的初始值都是false。每个位 set 都有一个当前大小,也就是该位 set 当前所用空间的位数。注意,这个大小与位 set 的实现有关,所以它可能随实现的不同而更改。位 set 的长度与位 set 的逻辑长度有关,并且是与实现无关而定义的。

1、如果指定了bitset的初始化大小,那么会把他规整到一个大于或者等于这个数字的64的整倍数。比如64位,bitset的大小是1个long,而65位时,bitset大小是2个long,即128位。做这么一个规定,主要是为了内存对齐,同时避免考虑到不要处理特殊情况,简化程序。

2:BitSet的size方法:返回此 BitSet 表示位值时实际使用空间的位数,值是64的整数倍

length方法:返回此 BitSet 的“逻辑大小”:BitSet 中最高设置位的索引加 1  

3.1 使用场景

常见的应用场景是对海量数据进行一些统计工作,比如日志分析、用户数统计等。例如:

有1千万个随机数,随机数的范围在1到1亿之间。将1到1亿之间没有在随机数中的数求出来

public static void main(String[] args) { Random random=new Random();  List<Integer> list=new ArrayList<>(); for(int i=0;i<10000000;i++) { int randomResult=random.nextInt(100000000); list.add(randomResult); } BitSet bitSet=new BitSet(100000000); for(int i=0;i<10000000;i++) { bitSet.set(list.get(i)); } System.out.println("0~1亿不在上述随机数中有"+bitSet.cardinality()); for (int i = 0; i < 100000000; i++) { if(!bitSet.get(i)) { System.out.println(i); } }  }


以上是关于过采样的原理的主要内容,如果未能解决你的问题,请参考以下文章

SMOTE算法原理 易用手搓小白版 数据集扩充 python

《Graph EmbeddingLINE:算法原理,实现和应用》

音频重采样实现原理

照片重复取样法计算原理

3相交流电压采样电路原理

彩色图像色彩空间原理