Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/40.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Arduino 简单的定点低通滤波器_Arduino_Embedded_Fixed Point - Fatal编程技术网

Arduino 简单的定点低通滤波器

Arduino 简单的定点低通滤波器,arduino,embedded,fixed-point,Arduino,Embedded,Fixed Point,我有一个简单的电路设置,通过LDR读取Arduino的亮度。我试图实现一个简单的低通滤波器来读取数据。如果analogRead返回一个无符号整数,如何最好地解决这个问题 我尝试过实现一个简单的定点表示,但不确定这是否是正确的方法 下面是一段代码片段: #define WLPF 0.1 #define FIXED_SHIFT 4 ldr_val = ((int)analogRead(A0)) << FIXED_SHIFT; while (true) { int newval

我有一个简单的电路设置,通过LDR读取Arduino的亮度。我试图实现一个简单的低通滤波器来读取数据。如果analogRead返回一个无符号整数,如何最好地解决这个问题

我尝试过实现一个简单的定点表示,但不确定这是否是正确的方法

下面是一段代码片段:

#define WLPF 0.1
#define FIXED_SHIFT 4

ldr_val = ((int)analogRead(A0)) << FIXED_SHIFT;
 while (true) {
    int newval = (int)analogRead(A0) << FIXED_SHIFT;
    ldr_val += WLPF*(newval - ldr_val);
    Serial.println(ldr_val >> FIXED_SHIFT, DEC);
}

注意,ADC的分辨率为10位,我使用的是8位Arduino Micro。

坚持整数算术:

#define WLPF 9

filtered = ((long)filtered * WLPF + newValue) / (WLPF + 1);

在没有FPU的设备上,而不是乘以0.1,在任何情况下,使其成为浮点而非定点实现,应除以10:

#define WLPF_DIV 10

...

ldr_val += (newval - ldr_val) / WLPF_DIV;
然而,8位处理器上的除法通常很昂贵,尽管与循环中Serial.println的执行时间相比可能相形见绌,但这是另一个问题。相反,选择2的幂次更有效,这样可以在右移的情况下执行除法

#define WLPF_SHIFT 3  // divide by 8

...

ldr_val += (newval - ldr_val) >> WLPF_SHIFT ;
使用有符号int是有问题的,因为有符号类型的右移是未定义的行为。在这种情况下,可以通过将代码更改为:

#define WLPF_DIV 8

... 

ldr_val += (newval - ldr_val) / WLPF_DIV ;
编译器很可能会发现两个常量的幂,并在任何情况下使用算术右移生成代码。但是,您最好重新考虑数据类型

在Serial.println调用中仍然有右移,但也可以替换为除以16:

#define WLPF_DIV 8
#define FIXED_MUL  16

ldr_val = (int)analogRead(A0) * FIXED_MUL  ;

for(;;)
{
    int newval = (int)analogRead(A0) * FIXED_MUL ;
    ldr_val += (newval - ldr_val) / WLPF_DIV 
    Serial.println(ldr_val / FIXED_MUL, DEC);
}

基于每个样本的数据的非确定性输出不会产生非常精确的滤波器,并且在任何情况下都会控制定时,因此您对频率响应几乎没有控制权,并且不会稳定。这也使得以前的性能优化变得毫无意义。如果它在您的应用程序中很重要,您可能想考虑一下这一点,但这是另一个问题。

我是从Hal Chamberlin的《微处理器的音乐应用》一书中转述出来的,第438页:

如果在累加器中允许使用大的数字,那么可以使用一次乘法和一些右移来生成一阶低通滤波器

  out = accum >> k
  accum = accum - out + in
选择“k”更改截止频率。移位越多,低通截止值越低,但累加器中的值越大。使用模拟读取的10位值,您可以轻松地右移4位,并且在累加器中仍有2位的净空,如上面提到的@datafiddler

Cypress为他们的PSOC芯片提供了一些应用程序注释,其中包含类似的等式,并使用了移位。我记得其中一个有一个很好的,与截止频率相关的移位数。 近似截止频率是采样频率除以2-pi乘以增益因数:

f0~fs/2pia

其中‘a’是二的幂


保持信号畅通

10位移位4得到14位,仍然是正数。你的低通公式对我来说很奇怪,虽然我对这个公式很有信心,但不确定我的定点实现。而且,在一个缓慢的8位MCU上使用浮点运算也是胡说八道。它不是个人电脑。我怀疑它是否有FPU。您可能不需要浮点数,或者您选择了错误的MCU来执行任务。@ CatsLoveJazz:在很多情况下,可以使用C++编译器编译编写成C代码的代码;但是它变成了C++代码。由于这个代码不会编译成C,因为使用串行对象只允许C++。添加到克利福德写的:即使你有相同语法的代码,它也将编译为两种语言,它可能有不同的语义,因此行为不同。C和C++的差异是可以找到的。一个简单的例子是常量限定符。还有一些更微妙。在使用16位整数算法的8位Arduino中,注意不要超过范围,尤其是在应用固定移位***常量无符号长WLPF=9时;这段代码在AVR上的性能非常差。无意冒犯,但嵌入式软件是一个非常特殊的领域,至少是问题所要求的领域。如果您对它没有经验,也不熟悉它的体系结构,最好不要回答。谢谢@Olaf,为什么这段代码在AVR上表现不好?乘以0.1等于除以10,所以0.1*newval-ldr_val与newval-ldr_val/10相同-您的表达式似乎与原始代码不等价。在任何情况下,在8位处理器上,除法和较小程度的乘法以及long的使用都会相对昂贵-尽管没有浮点运算那么昂贵,而且考虑到循环中的Serial.println调用,这在很大程度上是不相关的。analogRead是迄今为止最慢的部分,而且,要真正获得低通滤波器,您需要一个定义的周期时间,或者将实际周期时间考虑在内,这样Olaf的建议可以节省一些除法周期,而不是等待周期。如果这有关系的话,奥拉夫说得很对。谢谢你非常清楚的回答。过滤器的要求为ve 因为我只是想降低一个值的速度,所以需要1-2秒才能达到基线。不过,我可能会考虑使用中断来实现更精确的计时。