7

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

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

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

任何人都知道一个易于实现的功能可以帮助解决这个问题吗?这是一个 C++ 项目(使用 Qt 4.5),只有最少的库。我更愿意留在整数域(以毫伏为单位显示 3300-4200 的电压)。

TIA 迈克

4

7 回答 7

7

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

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

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

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

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

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

于 2011-04-01T17:33:24.390 回答
2

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

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

于 2011-04-01T17:26:52.340 回答
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;
于 2011-04-01T17:31:02.753 回答
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 秒的采样数据,同时仍然每秒获得一个结果,几乎没有额外的内存用法。

显然,您可以更改周期、移动平均长度和采样率,以便以您需要的更新频率获得您需要的结果。我建议您在需要更新的时间段内尽可能多地采样,然后根据您的意愿制作移动平均线。

于 2011-04-01T19:48:55.707 回答
0

这对我来说真的听起来像是一个硬件问题。是锂电池还是镍氢电池?放电曲线是什么样子的?电池单元和您的 ADC 之间有哪些组件?在实施各种数字滤波器之前,您需要了解这些事情。

于 2011-04-02T19:15:41.630 回答
0

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

于 2011-04-01T17:32:06.180 回答
-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);
}

于 2015-12-26T19:42:15.570 回答