3、时域分析基础:均值、方差、标准差、自相关与互相关

各位同学,咱们今天聊聊时域分析。说白了,就是看信号随时间怎么变。我刚开始做嵌入式那会儿,总觉得时域分析太基础,不就是算个平均值嘛。后来踩过坑才明白,这些基础统计量,才是信号处理的真正基石。

3.1 均值——信号的直流分量

均值,大家都会算。但你知道它代表什么物理意义吗?

对于传感器信号,均值就是信号的直流分量。比如一个温度传感器,在恒温环境下采集100个点,均值就是那个温度值。但实际项目中,均值往往藏着陷阱。

我曾经踩过的坑: 有一次做加速度传感器项目,采集到的数据均值一直在漂移。我以为是传感器坏了,折腾了两天。后来发现是电路板上的电容漏电,引入了直流偏置。所以,均值异常时,先检查硬件,别急着怀疑算法。

均值的计算公式很简单:

μ = (1/N) * Σ x[i]

其中N是样本数,x[i]是第i个样本。嗯,就这么简单。

3.2 方差与标准差——信号的波动程度

方差衡量的是信号偏离均值的程度。标准差是方差的平方根,单位跟原始信号一致,更直观。

我个人的习惯是:先看均值,再看标准差。均值告诉你信号的中心在哪,标准差告诉你信号抖不抖。

实战经验: 在电机转速监测项目中,我通过标准差来判断电机是否平稳。正常运行时标准差很小,一旦轴承磨损,标准差会明显增大。这个指标比单纯看均值灵敏多了。

方差公式:

σ² = (1/N) * Σ (x[i] - μ)²

标准差公式:

σ = sqrt(σ²)

你想想看,如果两个传感器测同一个物理量,均值一样,但标准差不同,你会选哪个?当然是标准差小的那个,因为它更稳定。

3.3 自相关——信号与自己的相似度

自相关函数,说白了就是信号跟自己的延迟版本做对比。它告诉你信号在不同时间点上的相关性。

我记得有一次做语音信号处理,需要检测基频。用自相关函数一算,峰值位置就是基频周期,简单又有效。

自相关公式:

Rxx(τ) = Σ x[t] * x[t + τ]

其中τ是延迟时间。

实用技巧: 自相关函数在τ=0时取最大值,等于信号的能量。如果自相关函数衰减很快,说明信号是随机的;如果衰减很慢,说明信号有周期性成分。

在嵌入式系统中,自相关常用于:

  • 检测信号的周期性
  • 估计信号的基频
  • 识别重复模式

3.4 互相关——两个信号的关系

互相关函数衡量两个信号之间的相似度。它比自相关多了一个维度——你可以比较两个不同的信号。

我做过一个项目,用两个麦克风定位声源。通过计算两个信号的互相关函数,找到峰值位置,就能算出时间差,进而定位声源方向。这个方法在嵌入式系统里很实用,计算量不大。

互相关公式:

Rxy(τ) = Σ x[t] * y[t + τ]

应用场景: 互相关在传感器融合中特别有用。比如用加速度计和陀螺仪做姿态估计,通过互相关可以分析两个传感器的时间延迟,然后做对齐处理。

3.5 实际项目中的选择策略

这么多统计量,什么时候用哪个?我给大家整理了一个表格:

统计量 用途 典型场景
均值 直流分量、基准值 温度测量、压力校准
标准差 噪声水平、稳定性 振动监测、故障检测
自相关 周期性检测 语音基频、心跳检测
互相关 信号对齐、时延估计 声源定位、多传感器融合

注意: 在嵌入式平台上,计算自相关和互相关时要注意内存和计算资源。我建议用定点数运算,避免浮点运算带来的性能开销。另外,数据长度不要超过1024点,否则实时性可能跟不上。

3.6 代码示例——嵌入式C实现

下面是我常用的一个轻量级实现,适合在MCU上跑:

// 均值计算
float calc_mean(float *data, int len) {
    float sum = 0;
    for (int i = 0; i < len; i++) {
        sum += data[i];
    }
    return sum / len;
}

// 标准差计算
float calc_std(float *data, int len, float mean) {
    float sum_sq = 0;
    for (int i = 0; i < len; i++) {
        float diff = data[i] - mean;
        sum_sq += diff * diff;
    }
    return sqrt(sum_sq / len);
}

// 自相关(简化版)
void calc_autocorr(float *data, int len, float *result, int max_lag) {
    for (int tau = 0; tau < max_lag; tau++) {
        float sum = 0;
        for (int i = 0; i < len - tau; i++) {
            sum += data[i] * data[i + tau];
        }
        result[tau] = sum;
    }
}

这段代码我用了好多年,简单可靠。你想想看,在资源受限的MCU上,这种实现方式最稳妥。

3.7 避坑指南

最后,分享几个我踩过的坑:

  • 数据长度要足够: 样本太少,统计量没有意义。我一般要求至少100个点。
  • 注意数据溢出: 在16位MCU上,累加和可能溢出。用32位变量或者分段计算。
  • 去直流再算相关: 计算自相关和互相关前,先减去均值。否则结果会被直流分量主导。
  • 归一化处理: 比较不同信号的相关性时,先做归一化,得到相关系数,范围在[-1, 1]之间。

嗯,时域分析基础就讲到这里。这些统计量看似简单,但用好了能解决很多实际问题。下一章咱们聊频域分析,到时候你会发现,时域和频域其实是同一枚硬币的两面。