用于电池电压显示的平滑功能可减少嵌入式系统中的尖峰

时间:2011-04-01 17:16:22

标签: c++ embedded battery

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

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

在阅读平滑算法时,似乎b样条,内核滤波器或其他一些平滑算法是理想的。但是,我找不到一个简单的例子,它不使用mathcad中的numpy或内部函数或其他一些函数。

有人知道一个简单易用的功能可以帮助解决这个问题吗?这是一个C ++项目(使用Qt 4.5),只有最小的库。我宁愿留在整数域(以3300-4200显示毫伏电压)。

TIA 麦克

7 个答案:

答案 0 :(得分:7)

嗯,如果没有具体的情况,告诉你需要什么有点困难。例如,您的传感器采样率是多少,以及您尝试去除的传感器波动和噪声如何表征?

但是,如果您已经实施了移动平均线,我可能会建议尝试移动中位数。 (最后一个 n 样本的中位数,而不是平均值。)这将减少输出中正常情况下大的短期畸变的影响。

如果你能找到有用的参数,那么使用某种形式的离散时间低通滤波器对CPU和内存的要求是优选的。这些很容易实现,只需要知道先前的输出值和当前输入来计算当前输出。例如:

Y = Y[n-1] + A * (X - Y[n-1])

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

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

答案 1 :(得分:2)

你可以在经典的NR书中找到解释和源代码: http://apps.nrbook.com/c/index.html

即第3章: http://www.arcetri.astro.it/irlab/library/recipes/bookcpdf/c3-3.pdf

答案 2 :(得分:1)

您是否考虑过对该值应用偏差限制?

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;

答案 3 :(得分:1)

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

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

它不是聪明,但它通常足以完成任务。这是一个例子和它的使用的测试模拟。

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 ;
} ;

// Test Simulation
#include <cstdlib>
#include <cstdio>
#include <windows.h>  // for Sleep to simulate sample rate
int main()
{
    // Average over 100 samples
    cPeriodicMean voltage_monitor( 100 ) ;

    for(;;)
    {
        // Simulate 4000mV +/- 50mV input
        int sample = 4000 + (std::rand() % 100) - 50 ;
        voltage_monitor.addSample( sample ) ;

        // Simulate 100Hz sample rate
        Sleep(10) ;

        // Current output
        int millivolts = voltage_monitor.getMean() ;
        printf( "\r%d millivolts    ", millivolts ) ;
    }
}

这项技术的改进将产生更平滑的输出但在相同频率下生成结果将使用周期平均输出作为移动平均滤波器的输入。如果您在100个采样周期内使用我的100个样本/秒示例,然后将其放入15个样本移动平均值,那么您将使用15秒的采样数据,同时仍然每秒获得一次结果,几乎没有额外的内存用法。

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

这对我来说真的听起来像硬件问题。它是Li-Io还是NiMH电池?放电曲线是什么样的?电池单元和ADC之间有哪些组件?在运行实现各种数字滤波器之前,你需要知道这些事情。

答案 6 :(得分:-2)

如果您没有答案,这是打印出来的好方法 在诸如pololu 3pi之类的机器人上。

{
    int bat = read_battery_millivolts();

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

    delay_ms(100);
}