C++ 嵌入式系统中降低电池电压峰值的平滑功能

C++ 嵌入式系统中降低电池电压峰值的平滑功能,c++,embedded,battery,C++,Embedded,Battery,在嵌入式设备中读取电池电压。然而,实际电压根据系统负载变化很大。我们需要一种减少电压波动的方法来显示最佳值 目前,我们使用的是滚动/移动平均线。然而,在过去的15次读数中,结果仍然波动过大 在阅读有关平滑算法的书籍时,b样条曲线、内核过滤器或其他一些平滑算法似乎是理想的。然而,我找不到一个简单的例子,在mathcad或类似的东西中不使用numpy或内在函数 有谁知道一个简单易实现的函数可以帮助实现这一点?这是一个C++项目(使用Qt 4.5),只有库的最小条数。我更愿意停留在整数域(显示3300

在嵌入式设备中读取电池电压。然而,实际电压根据系统负载变化很大。我们需要一种减少电压波动的方法来显示最佳值

目前,我们使用的是滚动/移动平均线。然而,在过去的15次读数中,结果仍然波动过大

在阅读有关平滑算法的书籍时,b样条曲线、内核过滤器或其他一些平滑算法似乎是理想的。然而,我找不到一个简单的例子,在mathcad或类似的东西中不使用numpy或内在函数

有谁知道一个简单易实现的函数可以帮助实现这一点?这是一个C++项目(使用Qt 4.5),只有库的最小条数。我更愿意停留在整数域(显示3300-4200之间的电压,单位为毫伏)

短暂性脑缺血发作
Mike

您可以在经典NR书籍中找到解释和源代码: ,

即第三章:

您是否考虑过对该值应用倾斜限制

new_val = Read_From_HW();
diff = new_val - prev_val;

if (diff > SKEW_LIMIT)
    diff = SKEW_LIMIT;
else if (diff < -SKEW_LIMIT)
    diff = -SKEW_LIMIT;

reported_val = prev_val + diff;
prev_val = reported_val;
new_val=Read_From_HW();
差异=新值-上一值;
如果(差异>倾斜限制)
diff=倾斜极限;
否则如果(差异<-偏斜限制)
diff=-偏斜极限;
报告值=上一个值+差异;
上一个值=报告的值;

我知道这并不能直接回答您的问题,但平均条形图会有帮助吗?换句话说,在15秒的窗口内显示最小值/最大值/平均值/中位数,而不是仅仅显示平均值。

好吧,如果没有具体情况,就很难说出你需要什么。例如,您的传感器采样率是多少,您试图消除的传感器波动和噪声是如何表征的

然而,如果您已经实现了移动平均值,我可能建议您尝试使用移动平均值。(最后n个样本的中位数,而不是平均值)这将有助于减少输出正常值的短期大像差的影响

如果您能找到有效的参数,那么对于CPU和内存需求来说,最好使用某种形式的离散时间低通滤波器。这些都很容易实现,只需要知道以前的输出值和当前输入即可计算当前输出。例如:

Y = Y[n-1] + A * (X - Y[n-1])
(其中,
Y
是当前输出,
Y[n-1]
是最后计算的输出,
X
是最新的传感器读数。)


A
实际上是低通滤波器的时间常数,但它是离散时间,因此它取决于采样率。具体来说,
A=dt/tau
,其中,
dt
是以秒为单位的采样周期,
tau
大致与连续时间常数有关。

可以深入研究信号处理技术和复杂数学,但你必须问问自己这是否真的必要

如果此显示是简单的瞬时数字输出,仅用于“指示”,而不是连续图形或数据日志(即,您不需要重建信号),则通常完全可以接受,只采用周期平均值而不是移动平均值。因为这不需要历史存储,所以您可以根据需要对任意多个样本进行平均,这将由所需的显示更新频率决定

它并不聪明,但通常足以胜任这项任务。下面是一个示例和一个使用它的测试模拟

class cPeriodicMean
{
    public :
        cPeriodicMean( int period ) : m_mean(0), 
                                      m_period(period),
                                      m_count(0),
                                      m_sum(0)
        { 
            // empty
        }

        void addSample( int sample )
        {
            m_sum += sample ;
            m_count++ ;
            if( m_count == m_period )
            {
                m_mean = m_sum / m_period ;
                m_count = 0 ;
                m_sum = 0 ;
            }
        }

        int getMean() 
        { 
            return m_mean ; 
        }

    private :
        int m_mean ;
        int m_period ;
        int m_count ;
        int m_sum ;
} ;

//测试模拟
#包括
#包括
#包括//用于模拟采样率的睡眠
int main()
{
//平均超过100个样品
CPERIODIC平均电压监测器(100);
对于(;;)
{
//模拟4000mV+/-50mV输入
int-sample=4000+(标准::兰德()%100)-50;
电压监测器添加样品(样品);
//模拟100Hz采样率
睡眠(10);
//电流输出
int毫伏=电压_监视器.getMean();
printf(“\r%d毫伏”,毫伏);
}
}
这项技术的一个改进将产生更平滑的输出,但产生相同频率的结果,即使用周期平均输出作为移动平均滤波器的输入。如果您使用我的100个样本/秒示例和100个样本周期,然后使用15个样本移动平均值,您将使用15秒的采样数据,同时仍然每秒获得一个结果,并且几乎没有额外的内存使用


显然,您可以更改周期、移动平均长度和采样率,以获得所需更新频率的结果。我建议您在需要更新的时间段内尽可能多地采集样本,然后尽可能长地进行移动平均。

这对我来说确实是一个硬件问题。它是锂离子电池还是镍氢电池?流量曲线是什么样的?电池和ADC之间有哪些组件?在开始使用各种数字滤波器之前,您需要了解这些情况。

如果您没有答案,这是一种打印内容的好方法 在机器人上,如pololu 3pi

{
    int bat = read_battery_millivolts();

    clear();
    print_long(bat);
    print("mV");
    lcd_goto_xy(0,1);
    print("Press B");

    delay_ms(100);
}

这太完美了。快速,易于计算。我花了一点时间计算出我的系数,但效果很好。@Mike:Matlab之类的工具(以及它的免费克隆,如FreeMat和Octave)有计算IIR滤波器系数的工具,可以满足特定的带宽要求。虽然在这种情况下只有一个系数,但经验方法可能就足够了(而且可能比学习Matlab更快)。您的移动平均方法是一个FIR滤波器的简单示例(所有系数都等于1)。感谢您,这正是我所寻找的,这是基于Arduino://compreSSor int compreSSor int compreSSor(int-signal,int-lastSignal){int-result=lastSignal+0.2*(signal-lastSignal);返回结果;}请注意
{
    int bat = read_battery_millivolts();

    clear();
    print_long(bat);
    print("mV");
    lcd_goto_xy(0,1);
    print("Press B");

    delay_ms(100);
}