自适应LMS算法的C语言时实实现

4 篇文章 2 订阅
4 篇文章 0 订阅

关于自适应LMS的理论基础已经非常的成熟,随便找一本关于自适应滤波器的书就会有介绍相关的内容,有的还可出了它的具体算法,但是还没有一本书有讲过怎样编写能够时实(Real Time)处理的基于C的自适应LMS算法(至少我没有见过),而这个应该说是将算法用于时实处理最为关键的一步,本次我就给大家分享一下关于我是如何用C语言来实现时实自适应LMS算法的。
说到时实处理,如果有自己写过时实FIR或者IIR滤波器程序的,可能对时实处理比较的熟悉,那么什么是时实处理呢?怎样来实现时实处理呢?


什么是实时处理?

简单来说,就是一个处理单元(一段程序代码)在某一时刻得到了输入的数据,然后立即处理得到的数据,并把要输出的数据送入输出单元进行输出,这样就完成了一次时实处理,然后等待下一时刻输入数据,然后重复上述过程。可以看到上述过程是“即刻采集即刻处理”,这就叫时实处理。而非时实处理就是先采集整个时间段的数据,采集完成后再进行处理,是“先采集后处理”所以是非时实的。


怎样实现时实处理?

上一段解释了什么是时实处理,下面来介绍怎样用处理器(比如单片机、DSP、ARM啥的)进行时实处理,通过上一段我们知道需要一个能够保证处理单元在特定的时刻进行处理的信号,这个信号可以有很多种形式,一般常用的就是利用处理器里面的定时器计数一段规定好的时间后然后产生一个溢出中断,并把处理单元的代码写到这个定时器中断程序中。这样定时器每隔一段时间就会就会进入一次中断并执行处理单元的程序,这样就实现了实时处理。


基于C的LMS算法

关于LMS相关的理论和Matlab仿真可以查看博客文章(浅谈自适应滤波器)这里不再赘述,下面将给出LMS算法的C语言代码
LMS.c文件

/*******************************************************************************************************************************************************************
 * 文件名:LMS.c
 * 作者:    IRONMAN
 * 日期:    2016.09.04
 * 版本:     V1.0
 * 说明:  此源文件基于c66x内核实现了最小均方算法(LMS)
 *
 * 算法递推形式:
 * 估计瞬时误差:        e(k) = d(k) - x'(k)w(k)                  (1)
 * 估计滤波系数矢量:w(k+1) = w(k) + 2 niu e(k)x(k)   (2)
 * 初始条件:                w(0) = 0;
 *
 * *****************************************************************************************************************************************************************/
#include <mathlib.h>
#include <dsplib.h>
#include <string.h>
#include "LMS.h"

#pragma DATA_ALIGN(lms_x, 8)
float lms_x[LMS_M];                                                         //输入信号向量存储数组
#pragma DATA_ALIGN(lms_w_forward, 8)
float lms_w_forward[LMS_M];                                       //用于存储未来一时刻滤波系数向量w(k+1)
#pragma DATA_ALIGN(lms_w, 8)
float lms_w[LMS_M];                                                       //用于存储当前时刻滤波系数矢量w(k)

Adaptive_Filter_In    lms_param_in;
Adaptive_Filter_Out  lms_param_out;

void LMS_Gradient_Instantaneous_Estimates(Adaptive_Filter_In *lms_in, Adaptive_Filter_Out* lms_out)
{
    int i;
    static int FIR_order;
    static unsigned char First_in_flag = 1;
    static float *w_ptr, *w_forward_ptr, *Temp_w_ptr;
    static float *x_ptr;
    float  temp;

    if(First_in_flag )
    {
        First_in_flag = 0;
        FIR_order = lms_in->length_x;

        memset((void *)lms_w_forward, 0, FIR_order*4);
        memset((void *)lms_w, 0, FIR_order*4);

        w_forward_ptr = lms_w_forward;
        w_ptr = lms_w;

        x_ptr = lms_in->x_ptr;
    }

/******************************************************************************************************************************/
/**************************************(1)式计算部分****************************************************************************/
/******************************************************************************************************************************/
    lms_out->y =  DSPF_sp_dotprod(x_ptr,w_ptr,FIR_order);               //一共需要82个时钟周期
    lms_out->error = lms_in->d - lms_out->y;
/******************************************************************************************************************************/
/**************************************(2)式计算部分****************************************************************************/
/******************************************************************************************************************************/
    temp = 2*LMS_NIU*lms_out->error;                                 //第二部分一共需要380个时钟周期

    for(i=0; i<FIR_order; i+=8)
    {
        w_forward_ptr[i]      = w_ptr[i]     + temp*x_ptr[i];
        w_forward_ptr[i+1] = w_ptr[i+1] + temp*x_ptr[i+1];
        w_forward_ptr[i+2] = w_ptr[i+2] + temp*x_ptr[i+2];
        w_forward_ptr[i+3] = w_ptr[i+3] + temp*x_ptr[i+3];
        w_forward_ptr[i+4] = w_ptr[i+4] + temp*x_ptr[i+4];
        w_forward_ptr[i+5] = w_ptr[i+5] + temp*x_ptr[i+5];
        w_forward_ptr[i+6] = w_ptr[i+6] + temp*x_ptr[i+6];
        w_forward_ptr[i+7] = w_ptr[i+7] + temp*x_ptr[i+7];
    }
/**************************************************************************************************************************************/
/************************************************************变量更替*******************************************************************/
/**************************************************************************************************************************************/
    Temp_w_ptr = w_forward_ptr;        //新旧滤波系数矢量指针交换
    w_forward_ptr = w_ptr;
    w_ptr = Temp_w_ptr;
}

LMS.h文件

#ifndef LMS_H_
#define LMS_H_

#include "Adaptive_filter.h"

#define LMS_M                                          16                //定义FIR滤波器的阶数   为保证高速运算须为8的倍数
#define LMS_NIU                                      0.0001              //定义收敛因子


extern float lms_x[LMS_M];
extern Adaptive_Filter_In    lms_param_in;
extern Adaptive_Filter_Out  lms_param_out;

extern void LMS_Gradient_Instantaneous_Estimates(Adaptive_Filter_In *lms_in, Adaptive_Filter_Out* lms_out);

#endif

Adaptive_filter.h文件

#ifndef ADAPTIVE_FITLER_H_
#define ADAPTIVE_FITLER_H_

//定义输入参数结构体
typedef struct
{
    float *x_ptr;         //指向输入数组矢量X的指针
    int length_x;        //矢量X的长度,也即FIR滤波器的长度
    float d;                //输入参考数据
}Adaptive_Filter_In;
//定义输出参数结构体
typedef struct
{
    float y;                //滤波器的输出
    float error;        //误差输出
}Adaptive_Filter_Out;


#endif /* ADAPTIVE_FITLER_H_ */

一共三个文件,一个C文件,两个h文件。LMS.c文件是算法的主要部分,LMS.h文件主要起到参数传递作用方便其它的模块调用,Adaptive_filter.h主要定义了自适应滤波器的输入和输出数据的结构体。
运行的处理器是TMS320C6678 DSP,下面将给出在上面运行的结果。


基于TMS320C6678运行结果

首先给出在主函数中调用和产生仿真信号的代码
实验的类型是信号增强,信号的类型和Matlab上的一样以便做对比。程序中调用了dsplib.h中的向量点乘运算函数,如果不是在DSP上或者没有DSPlib的可以自己编写该函数,也不难。

#include <c6x.h>
#include <stdio.h>
#include <string.h>
#include <mathlib.h>
#include <dsplib.h>
#include "KeyStone_common.h"
#include "Keystone_DDR_Init.h"
#include "LMS.h"

#define PI                            3.141592654
#define Fs                            10000
#define Ts                            1/Fs
#define F1                           2
#define F2                           10
#define F3                           20
#define F4                           500
#define NUM_OF_POINT    (32*1024)

#pragma DATA_SECTION(signal, ".DDR_buffer");
float signal[NUM_OF_POINT];
#pragma DATA_SECTION(noise, ".DDR_buffer");
float noise[NUM_OF_POINT];
#pragma DATA_SECTION(signal_noise, ".DDR_buffer");
float signal_noise[NUM_OF_POINT];
#pragma DATA_SECTION(out_error, ".DDR_buffer");
float out_error[NUM_OF_POINT];
#pragma DATA_SECTION(out_y, ".DDR_buffer");
float out_y[NUM_OF_POINT];

//init the system
void System_init(void)
{
    KeyStone_common_CPU_init();/*enable TSC, memory protection interrupts, EDC for internal RAM;clear cache; protect L1 as cache*/
    KeyStone_common_device_init();/*print device information. Enable memory protection interrupts, EDC for MSMC RAM*/
    KeyStone_Exception_cfg(TRUE);//enable exception handling
    CACHE_setL1PSize(CACHE_L1_32KCACHE);
    CACHE_setL1DSize(CACHE_L1_32KCACHE);
    CACHE_setL2Size(CACHE_0KCACHE);
    KeyStone_main_PLL_init(100, 12, 1);   //DSP core speed: 100*12/1=1200MHz
    KeyStone_PASS_PLL_init(100, 21, 2);
    KeyStone_DDR_init(66.66667, 20, 1, NULL);       //DDR init
}

void make_data(void)
{
    int i;
    for(i=0;i<NUM_OF_POINT;i++)
    {
        signal[i] = sinsp(2*PI*F1*i*Ts) + 0.5*sinsp(2*PI*F2*i*Ts) + 0.25*sinsp(2*PI*F3*i*Ts);
        noise[i] = 5*sinsp(2*PI*F4*i*Ts+PI/2);
        signal_noise[i] = signal[i] + 0.2*noise[i];
        if(i>0)
            signal_noise[i] += 0.15*noise[i-1];
        if(i>1)
            signal_noise[i] += 0.1*noise[i-2];
    }
}

void  main(void)
{
    int i, j;
    System_init();
    make_data();
    memset((void *)lms_x, 0, sizeof(lms_x)*sizeof(float));
    for(i=0;i<NUM_OF_POINT;i++)
    {
        if(i<LMS_M)
        {
            for(j=0;j<=i;j++)
            {
                lms_x[j] = noise[i-j];
            }
        }
        else
        {
            for(j=0;j<LMS_M;j++)
            {
                lms_x[j] = noise[i-j];
            }
        }
        lms_param_in.d = signal_noise[i];
        lms_param_in.x_ptr = &lms_x[0];
        lms_param_in.length_x = LMS_M;

        LMS_Gradient_Instantaneous_Estimates(&lms_param_in, &lms_param_out);                    //运行瞬时梯度估计LMS算法 耗时514个时钟周期

        out_error[i] = lms_param_out.error;
        out_y[i] = lms_param_out.y;
    }
    while(1);
}

可以在代码中的一些注释看到一些过程执行需要的时钟数,CPU主频是1.2GHz,可以让大家对一次运行的时间有一个感性的认识。实验处理的点数是(32*1024)个,由于CCS上自带的绘图功能一次最多只能绘制5000个浮点数据,所以下面是给出的是用0到5000个数据点在CCS上绘制的结果图
信号
该图显示的是产生的仿真信号
噪声
该图显示的是产生的噪声也即干扰
信号加噪声
该图是将得到的噪声通过一个线性系统后和信号相加后得到的图形
输出误差
该图是滤波器输出的误差(因为做的是信号的增强,所以我们感兴趣的信号是输出的误差)
输出
该图是滤波器的输出


可以和Matlab仿真的结果进行对比,会发现它们有一些不同但差别不是很大,主要原因是运算的精度不同,在Matlab上是double float 型的,在6678上的是single float型的。

  • 18
    点赞
  • 146
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 26
    评论
LMS 算法是一种自适应滤波算法,常用于陷波器的实现。陷波器是一种信号处理器件,用于消除信号中的频率成分。在信号处理领域中,噪声比较常见,因此需要进行噪音滤波。LMS 算法就是一种可以进行自适应陷波,也就是自动调整陷波器参数的算法LMS 算法实现自适应陷波器的步骤如下: 1. 初始化陷波器参数:包括陷波器系数和输入信号缓存数组。 2. 对于每个输入样本,在输入信号缓存数组中加入新的样本,并调整陷波器系数。 3. 在陷波器系数调整时,需要进行误差计算和更新。误差计算是输出样本和期望结果之间的差值,用于衡量陷波器的表现。更新陷波器系数时,通过乘以步长和误差进行调整。 4. 不断重复过程,直到达到预设的收敛要求。 在 C 语言中,LMS 算法实现自适应陷波器的代码如下: ``` #include <stdio.h> #define NUM_SAMPLES 100 #define FILTER_LENGTH 10 #define MU 0.1 int main() { int input_signal[NUM_SAMPLES]; int output_signal[NUM_SAMPLES]; int filter_coef[FILTER_LENGTH] = {[0 ... FILTER_LENGTH - 1] = 1}; int filter_input[FILTER_LENGTH]; int error_signal; for (int i = 0; i < NUM_SAMPLES; i++) { int input_sample = get_input_sample(); // 获取输入样本 input_signal[i] = input_sample; error_signal = input_sample; // 初始误差为输入样本 for (int j = 0; j < FILTER_LENGTH; j++) { filter_input[j] = input_signal[i - j]; // 更新输入缓存 } int output_sample = filter_coefficient(filter_input, filter_coef, FILTER_LENGTH); // 计算输出样本 output_signal[i] = output_sample; error_signal -= output_sample; // 计算误差 for (int j = 0; j < FILTER_LENGTH; j++) { filter_coef[j] += MU * error_signal * filter_input[j]; // 更新陷波器系数 } } return 0; } ``` 以上代码简单地说明了 LMS 算法实现自适应陷波器的流程,但是实际情况下,需要根据实际应用场景进行具体的参数调整和性能优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_IRONMAN_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值