为什么我的 NLMS 过滤器减少了 +/- 2?

信息处理 lms
2022-02-05 04:58:09

我已经有一段时间没有进行任何信号处理了,但是我完全被归一化最小均方滤波器实现中的不稳定性和偏差所困扰。

我检查了通常的事情:

  1. 我实际上是否接近收敛下限(测试信号中 AWGN 的方差)?不。
  2. 我的步长是否低于(其中只是用作瞬时估计的信号的外积)?是的。2trace[R]R
  3. 我的窗口过去样本的点积是否使用增加延迟指数的约定(“反向”顺序)?是的。
  4. 当我计算预测值时,我使用的是以前的信号索引,而不是当前的吗?是的。

我通过实现一个简单的一键过滤器缩小了我的问题。我的参考是 Haykin's Adaptive Filter Theory第 324 页上的算法,我正在使用第 280 页上的相同测试条件:

  • 输入:一阶 AR 过程,,噪声方差a=0.99σu2=0.93627
  • 试验次数:100 次试验
  • 信号长度:500 个样本

错误

根据 Haykin 的说法,在少于 100 个样本后,我应该低于这不是我所看到的,但我相信它必须是基本的。101

以下是 MMSE 和平均绝对滤波器系数误差,可视化:

误差图

MMSE 不会呈指数下降,并且滤波器系数与实际 AR 过程值的距离永远不会小于 2。

而事实上,MMSE 仍然比信号末端附近的噪声方差要远得多:

[10.260998119102881, 16.060967587632792, 41.72963567568045, 49.232814770283696, 50.56247908944245, 84.29056731939356, 99.74962522053087, 74.94509263319398, 61.55708377260941, 43.79321013379245, 47.68084407515727, 51.84551183735604, 23.977556992678352, 23.691841969230886, 15.292586007185905, 12.710425182987175, 14.689863202271942, 15.03357374836029, 16.305144820997945, 21.770075252230768, 16.501486407946132, 21.62300216817481, 15.461623713499273, 26.354991476964795, 28.001121132610393, 21.632068009342056, 15.812522324768985, 15.522990356558456, 53.24514663841896, 9.36613637386399, 52.24856602614516, 98.50333720012102, 251.59105479441695, 193.85612892514402, 124.53671103656986, 159.16459102635278, 82.2544192379602, 50.25510472853462, 67.03420998875504, 9.59758344071307, 9.770861267272064, 21.975670899135086, 28.84699022842896, 29.070465314026674, 15.05395348320232, 20.20596174124456, 30.609319675498526, 14.396998393433874, 17.654986587869196, 9.664719322902583]

过滤代码

为了产生上述结果,我尝试实现最简单的 1-tap NLMS 过滤器,如下所示。

extern crate rand;

use std::f64::EPSILON;
use rand::distributions::{IndependentSample, Normal};

extern crate gnuplot;

use gnuplot::{Figure, Caption, Color};


fn main() {
  let trial_count = 100;
  let signal_len = 500;

  let noise_std_dev = 0.96761;
  // AR process coefficient [n-1]
  let process = [-0.99];

  let mut rand_gen = rand::OsRng::new().unwrap();

  let mut mmse: Vec<f64> = vec![0.0; signal_len];
  let mut avg_filt_err: Vec<f64> = vec![0.0; signal_len];

  let noise_dist = Normal::new(0.0, noise_std_dev);

  for _ in 0..trial_count {

    let mut sig_vec: Vec<f64> = Vec::with_capacity(signal_len);

    // Zero padding to start noise process
    sig_vec.push(0.0);

    for n in 1..signal_len {
      let noise = noise_dist.ind_sample(&mut rand_gen);
      let p1 = if n > 0 {
        -process[0]*sig_vec[n-1]
      } else {
        0.0
      };
      sig_vec.push(noise + p1);
    }

    let step_size = 0.05;

    let mut pred_vec: Vec<f64> = Vec::with_capacity(signal_len);

    let mut filt_coef: f64 = 0.0;    

    // Zero padding to start filter process
    pred_vec.push(0.0);

    for sig_ind in 1..signal_len {
      let pred = filt_coef * sig_vec[sig_ind - 1];
      pred_vec.push(pred);
      let err = sig_vec[sig_ind] - pred;
      mmse[sig_ind] += err.powi(2) / trial_count as f64;
      filt_coef += step_size * sig_vec[sig_ind - 1] * err /
        (sig_vec[sig_ind - 1].powi(2) + EPSILON);
      avg_filt_err[sig_ind] += (process[0] - filt_coef).abs() / trial_count as f64;
    }
  }

  let x: Vec<usize> = (0..mmse.len()).collect();
  let mut fg = Figure::new();
  fg.axes2d()
    .lines(&x, &mmse, &[Caption("MMSE"), Color("black")]);

  let mut fg2 = Figure::new();
  fg2.axes2d()
     .lines(&x, &avg_filt_err, &[Caption("Average filter error"), Color("red")]);
  fg.show();
  fg2.show();
}
1个回答

TL,DR总结:

  • process[0]由于信号生成 if 语句中的减号,您的代码有误。
  • 一旦对此进行了校正,自适应滤波器似乎在所有情况下都会收敛。
  • 您没有看到 Haykin 关于 MMSE 的说明的原因是您没有使用所需的信号来形成错误。如果您没有按照 MMSE 分析假设的方式实施算法,那么所有的赌注都将失败;您将不得不重新进行分析。d[n]

所以我试图将你的原始代码翻译成R(因为这是我目前主要使用的),我的第一个猜测似乎接近正确。

如果我在运行结束时使用原始代码进行绘图avg_filt_err,那么我会得到下面的第一张图片。如果我在设置信号时更改符号(正如我的第一个猜测所建议的那样),那么错误会显着减少(见下图第二张)。

这意味着我改变了

  p1[n] <-  if (n > 1) -process[1]*sig_vec[n-1] else 0.0

  p1[n] <-  if (n > 1) process[1]*sig_vec[n-1] else 0.0

即只是process[1]更改了登录(R是 1-indexed,因此[1]不是[0]来自您的代码)。

第三和第四张图片显示了实际的系数变化。它似乎-0.99在第二种情况下收敛到接近,在第一种情况下发散。

原码错误

修改 #1 后出错

原始代码中的系数变化

修改后的系数变化 #1

如果我提出第二个建议,并使用代替接收到的信号:d(n)

所以改变

 err <- sig_vec[sig_ind] - pred

 err <- p1[sig_ind] - pred

(在p1生产时保存后)然后它得到了我所期望的。系数图的误差和收敛如下。

修改 #1 和 #2 后出错。

在此处输入图像描述

最后,要使用嘈杂的接收信号(您在评论下方的建议),则需要将错误更改为:

err <- sig_vec[sig_ind] - pred

在这种情况下,您可以看到为什么这不是一个很好的解决方案:它可以工作,但噪声会继续使估计值偏离,并且必须重新收敛。上图是误差,下图是系数值。

使用有噪声的接收信号的错误

系数估计


仅在下面的 R 代码

# extern crate rand;
#
#use std::f64::EPSILON;
EPSILON <- 10^(-6)
#use rand::distributions::{IndependentSample, Normal};
#
#extern crate gnuplot;
#
#use gnuplot::{Figure, Caption, Color};

#
#fn main() {
#  let trial_count = 100;
trial_count <- 100
#let signal_len = 500;
signal_len <- 500

#  let noise_std_dev = 0.96761;
noise_std_dev <- 0.96761
#  // AR process coefficient [n-1]
#let process = [-0.99];
process <- rep(-0.99, signal_len)

#let mut rand_gen = rand::OsRng::new().unwrap();

#let mut mmse: Vec<f64> = vec![0.0; signal_len];
mmse <- rep(0.0, signal_len)
#let mut avg_filt_err: Vec<f64> = vec![0.0; signal_len];
avg_filt_err <- rep(0.0, signal_len)

#let noise_dist = Normal::new(0.0, noise_std_dev);

 # for _ in 0..trial_count {
for (idx in 1:(trial_count+1 )) 
{
#    let mut sig_vec: Vec<f64> = Vec::with_capacity(signal_len);
    sig_vec <- rep(0.0, signal_len)

#    // Zero padding to start noise process
#    sig_vec.push(0.0);
    sig_vec[1] <- 0.0

    p1 <- rep(0.0, signal_len)

#    for n in 1..signal_len {
    for (n in 1:signal_len)
    {
#      let noise = noise_dist.ind_sample(&mut rand_gen);
      noise <- rnorm(1, 0, noise_std_dev)
#      let p1 = if n > 0 {
#        -process[0]*sig_vec[n-1]
#      } else {
#        0.0
#      };
      # was: 
      #p1[n] <-  if (n > 1) -process[1]*sig_vec[n-1] else 0.0
      p1[n] <-  if (n > 1) process[1]*sig_vec[n-1] else 0.0

#      sig_vec.push(noise + p1);
      sig_vec[n] <- noise + p1[n]
    }

#    let step_size = 0.05;
    step_size <- 0.05

#    let mut pred_vec: Vec<f64> = Vec::with_capacity(signal_len);
    pred_vec <- rep(0, signal_len)

#    let mut filt_coef: f64 = 0.0;    
    filt_coef <-  0.0

#    // Zero padding to start filter process
#    pred_vec.push(0.0);
    pred_vec[1] <- 0.0

#    for sig_ind in 1..signal_len {
    for (sig_ind in seq(2,signal_len))
    {
      #let pred = filt_coef * sig_vec[sig_ind - 1];
      pred <-  filt_coef * sig_vec[sig_ind - 1]
      #pred_vec.push(pred);
      pred_vec[sig_ind] <- pred
      #let err = sig_vec[sig_ind] - pred;
      err <- sig_vec[sig_ind] - pred
      #mmse[sig_ind] += err.powi(2) / trial_count as f64;
      mmse[sig_ind] <-  mmse[sig_ind] + err^2 / trial_count
      #filt_coef += step_size * sig_vec[sig_ind - 1] * err /
      #  (sig_vec[sig_ind - 1].powi(2) + EPSILON);
      filt_coef <- filt_coef + step_size * sig_vec[sig_ind - 1] * err /
        (sig_vec[sig_ind - 1]^2 + EPSILON)
      #avg_filt_err[sig_ind] += (process[0] - filt_coef).abs() / trial_count as f64;
      avg_filt_err[sig_ind] <-  avg_filt_err[sig_ind] + abs((process[1] - filt_coef)) / trial_count ;
    }

#  let x: Vec<usize> = (0..mmse.len()).collect();
#  let mut fg = Figure::new();
#  fg.axes2d()
#  .lines(&x, &mmse, &[Caption("MMSE"), Color("black")]);
#  
#  let mut fg2 = Figure::new();
#  fg2.axes2d()
#  .lines(&x, &avg_filt_err, &[Caption("Average filter error"), Color("red")]);
#  fg.show();
#  fg2.show();
}

plot(avg_filt_err)

下一个猜测:您似乎没有无噪音的“所需”信号?您正在err使用嘈杂的接收信号进行成型。您应该使用无噪声信号。

参见 Haykin 的图 6.1,“自适应滤波器理论”,第四版,Prentice-Hall,p321。

在此处输入图像描述

该图中没有任何地方

...所需信号只是 LMS 试图预测的时间步长的实际信号...

你能告诉我它在哪里说的吗?

原因是因为 LMS 算法及其变体常用于移动的信道识别:移动系统在传输开始时发送一个已知的数据序列,因此d(n)在图中可以知道传输的两端---无噪音。

使用实际接收到的数据充满危险。


我认为该过程的计算不正确。“2 的差”听起来很可疑,就像 NLMS 得到的答案是 0.99 而不是 -0.99。

尝试更改此行:

 -process[0]*sig_vec[n-1]

 process[0]*sig_vec[n-1]

在设置中sig_vec并让我们知道结果。