LMS(Least Mean Square)最小均方算法是一种经典的自适应滤波算法,因其计算简单、易于实现而广泛应用于 DSP 系统中,主要用于噪声消除、系统辨识、信道均衡等场景。
一、LMS 算法基本原理
LMS 算法通过不断调整滤波器的权值系数,使输出信号与期望信号之间的均方误差最小化。其核心迭代公式为:

二、针对 DSP 的 C 语言实现
1. 定点数实现(推荐用于大多数 DSP)
大多数 DSP(如 TI C2000、C5000 系列)是定点处理器,使用定点数运算可以获得更高的执行效率。
cpp
#include <stdint.h>
// LMS滤波器结构体
typedef struct {
int16_t *w; // 权值系数数组 (Q15格式)
int16_t *x; // 输入信号延迟线
uint16_t order; // 滤波器阶数
int16_t mu; // 步长因子 (Q15格式)
uint16_t index; // 延迟线索引
} LMS_Filter;
/**
* @brief 初始化LMS滤波器
* @param lms LMS滤波器结构体指针
* @param w 权值系数数组(初始化为0)
* @param x 输入信号延迟线数组(初始化为0)
* @param order 滤波器阶数
* @param mu 步长因子 (Q15格式)
*/
void LMS_Init(LMS_Filter *lms, int16_t *w, int16_t *x, uint16_t order, int16_t mu) {
lms->w = w;
lms->x = x;
lms->order = order;
lms->mu = mu;
lms->index = 0;
// 初始化权值和延迟线为0
for (uint16_t i = 0; i < order; i++) {
lms->w[i] = 0;
lms->x[i] = 0;
}
}
/**
* @brief LMS滤波器处理一个样本
* @param lms LMS滤波器结构体指针
* @param input 输入信号样本 (Q15格式)
* @param desired 期望信号样本 (Q15格式)
* @return 滤波器输出 (Q15格式)
*/
int16_t LMS_Process(LMS_Filter *lms, int16_t input, int16_t desired) {
int32_t y = 0; // 滤波器输出 (Q30格式)
int32_t e; // 误差信号 (Q30格式)
int16_t output; // 最终输出 (Q15格式)
uint16_t i, j;
// 更新输入信号延迟线(循环缓冲区方式)
lms->x[lms->index] = input;
// 计算滤波器输出(利用DSP的MAC指令优化)
j = lms->index;
for (i = 0; i < lms->order; i++) {
y += (int32_t)lms->w[i] * lms->x[j];
if (j == 0) {
j = lms->order - 1;
} else {
j--;
}
}
// 转换为Q15格式
output = (int16_t)(y >> 15);
// 计算误差信号
e = ((int32_t)desired << 15) - y;
// 更新权值系数
j = lms->index;
for (i = 0; i < lms->order; i++) {
// w(n+1) = w(n) + μ * e(n) * x(n-i)
// 所有运算均为Q15格式,结果右移30位
lms->w[i] += (int16_t)(((int32_t)lms->mu * e * lms->x[j]) >> 30);
if (j == 0) {
j = lms->order - 1;
} else {
j--;
}
}
// 更新循环缓冲区索引
lms->index++;
if (lms->index >= lms->order) {
lms->index = 0;
}
return output;
}
2. 浮点数实现(适用于浮点 DSP)
对于 TI C6000、ARM Cortex-M4/M7 等带有 FPU 的 DSP,可以使用浮点数实现,代码更简洁直观:
cpp
#include <stdint.h>
// LMS滤波器结构体
typedef struct {
float *w; // 权值系数数组
float *x; // 输入信号延迟线
uint16_t order; // 滤波器阶数
float mu; // 步长因子
uint16_t index; // 延迟线索引
} LMS_Filter_Float;
/**
* @brief 初始化浮点LMS滤波器
*/
void LMS_Init_Float(LMS_Filter_Float *lms, float *w, float *x, uint16_t order, float mu) {
lms->w = w;
lms->x = x;
lms->order = order;
lms->mu = mu;
lms->index = 0;
for (uint16_t i = 0; i < order; i++) {
lms->w[i] = 0.0f;
lms->x[i] = 0.0f;
}
}
/**
* @brief 浮点LMS滤波器处理一个样本
*/
float LMS_Process_Float(LMS_Filter_Float *lms, float input, float desired) {
float y = 0.0f;
float e;
uint16_t i, j;
// 更新输入信号延迟线
lms->x[lms->index] = input;
// 计算滤波器输出
j = lms->index;
for (i = 0; i < lms->order; i++) {
y += lms->w[i] * lms->x[j];
if (j == 0) {
j = lms->order - 1;
} else {
j--;
}
}
// 计算误差信号
e = desired - y;
// 更新权值系数
j = lms->index;
for (i = 0; i < lms->order; i++) {
lms->w[i] += lms->mu * e * lms->x[j];
if (j == 0) {
j = lms->order - 1;
} else {
j--;
}
}
// 更新循环缓冲区索引
lms->index++;
if (lms->index >= lms->order) {
lms->index = 0;
}
return y;
}
三、DSP 优化关键技术
1. 利用 DSP 的 MAC 指令
大多数 DSP 都有单周期乘累加(MAC)指令,可以显著提高 LMS 算法的执行速度。在 C 语言中,可以通过以下方式优化:
- 使用循环缓冲区(Circular Buffer)避免数据移动
- 确保数据对齐到 DSP 的字边界
- 使用编译器优化选项(如 TI 的
-o3 -mf3) - 对于关键循环,可以使用内联汇编直接调用 MAC 指令
2. 定点数格式选择
- Q15 格式:适用于 16 位 DSP,动态范围为 [-1, 1)
- Q31 格式:适用于 32 位 DSP,动态范围更大
- 步长因子\(\mu\)通常选择较小的值(如 0.01~0.1),以保证算法稳定性
3. 防止溢出
- 在定点运算中,中间结果使用 32 位累加器
- 对权值系数进行限幅(Clipping)
- 适当调整输入信号的幅度
四、使用示例:自适应噪声消除
cpp
#include <stdio.h>
#include <math.h>
#define FILTER_ORDER 32
#define MU 0.01f // 步长因子
// 模拟输入信号:正弦波+噪声
float generate_signal(float t, float noise_amp) {
float signal = sinf(2 * M_PI * 100 * t); // 100Hz正弦波
float noise = noise_amp * (rand() / (float)RAND_MAX * 2 - 1); // 白噪声
return signal + noise;
}
int main() {
float w[FILTER_ORDER];
float x[FILTER_ORDER];
LMS_Filter_Float lms;
float t = 0.0f;
float dt = 1.0f / 8000.0f; // 8kHz采样率
// 初始化LMS滤波器
LMS_Init_Float(&lms, w, x, FILTER_ORDER, MU);
// 模拟处理1秒数据
for (int i = 0; i < 8000; i++) {
// 生成带噪声的输入信号
float noisy_signal = generate_signal(t, 0.5f);
// 生成参考噪声(与输入噪声相关)
float reference_noise = generate_signal(t, 0.5f) - sinf(2 * M_PI * 100 * t);
// LMS滤波:期望信号是带噪声的信号,输入是参考噪声
// 输出将是噪声的估计值,误差就是去噪后的信号
float noise_estimate = LMS_Process_Float(&lms, reference_noise, noisy_signal);
float clean_signal = noisy_signal - noise_estimate;
// 输出结果(实际应用中可发送到DAC)
printf("%f\n", clean_signal);
t += dt;
}
return 0;
}
五、参数选择指南
-
滤波器阶数 N:
- 阶数越高,滤波效果越好,但计算量越大
- 通常选择 32~256 阶,根据应用需求调整
-
步长因子 μ:

-
收敛时间:
- 大约需要10--20倍滤波器阶数的样本数才能收敛
六、常见问题与解决方法
