浮点中的移动平均滤波器实现

信息处理 数字滤波器 C++ 移动平均线 浮点
2022-02-03 09:12:50

我有以下移动平均数字滤波器的 C++ 实现。

template<uint32_t WINDOW_SIZE>
class MovingAverage{
    public:
        MovingAverage(){
            for(uint32_t elem = 0; elem < WINDOW_SIZE; elem++){
                buffer[elem] = 0;
            }
            input_value = 0;
            sum = 0;
            average = 0;
            index = 0;
        }
        
        void setInput(float input){
            input_value = input;
        }
        
        void calculate(){
            sum -= buffer[index];
            buffer[index] = input_value;
            sum += buffer[index];
            average = sum/WINDOW_SIZE;
            index = (index + 1) % WINDOW_SIZE;
        }
        
        float getOutput(){
            return average;
        }
        
    private:
        float buffer[WINDOW_SIZE];
        float input_value;
        float sum;
        float average;
        uint32_t index;
};

我在 ARM Cortex A9 平台上遇到了一个奇怪的行为。如果我首先将来自 adc 的值(这些值在 514 左右波动)传递到窗口大小等于 32 个样本的移动平均值中,然后我将值 128 强制传递给滤波器,然后滤波器输出值约为 126 而不是 128 . 此时buffer在其所有项目中包含值 128,但sum包含与预期不同的值32*128如果我在我的 PC 上运行过滤器算法,它会按预期工作。有谁知道这种行为的原因是什么?

1个回答

好吧,514 足够接近 2 的幂,当浮点数超过 2 的幂时,它会失去一位数的精度。

浮点数将使用 24 位尾数存储。

你的总和将在215, 当sum小于215,您的输入将被精确累积21424=210, 如果sum累加器大于或等于215,然后指数从1415和那个位210是四舍五入的,系统地执行数千次迭代将累积到您观察到的错误。

E=211, 和1/2rand()<1/2一个统一的随机数,并设置avg.step输入并计算,并返回. 重复以下操作。sumbuffer

    delta = E if s < 512 else -E
    s = avg.step(512 + delta)
    s = avg.step(512 + (-0.5 <= rand() <0.5))

累积的误差是这样演变的

s进化

如果您没有跨越两个边界的幂,则不应看到相同的行为。您可以牺牲几位精度来避免这种累积的错误。

一个快速的解决方案是将总和初始化为不同的值,然后在计算平均值时减去该值。对于有符号输入sum = 3 * WINDOW_SIZE * MAX_INPUT,然后对于介于-MAX_INPUT和之间MAX_INPUT的输入,总和将保持在用相同指数表示的数字范围内。For unsignedMAX_INPUT * WINDOW_SIZE为您提供了额外的位精度。这有效地使您的计算定点。