简介针对LMS与RLS自适应均衡算法资源包提供MATLAB 2021a及更高版本下的仿真对比实现。面向通信工程、信号处理方向的学生与工程师可用于理解多径信道与频率选择性衰落下的均衡器设计比较两种算法在收敛速度、稳态误差和计算复杂度上的差异也适用于无线通信基带处理、水声通信等需要自适应均衡的场景。包内共4个文件包括2个MATLAB脚本、1份算法分析文档和1份FPGA与MATLAB协同设计说明整体压缩包约74KB结构简洁便于直接运行与查阅。目前已有271人学习下载。通过仿真源码可调整信道参数与滤波器阶数直观对比两类算法在不同信噪比下的误码率与收敛曲线配套文档补充了LMS与RLS的数学推导、滤波器阶次与步长因子的参数优化建议以及从MATLAB仿真模型到FPGA硬件实现的映射方法适合从仿真走向实际部署时参考。1. 为什么拿 LMS 和 RLS 比自适应均衡器的“收敛-稳态”天平信道衰落和多径造成的码间干扰ISI是通信系统里绕不开的黑匣子。要把它压下去最经济的做法就是在接收端挂一个自适应均衡器用已知训练序列让抽头系数自动收敛到逆信道响应。LMS 结构简单、每步只需两次乘法但步长选小了收敛慢、选大了稳态误差大RLS 借助矩阵递推收敛速度快一个量级以上但计算量多一个维度遗忘因子稍微调错就可能发散。这篇内容我用 MATLAB 2021a 把两个算法做在同一组信道数据上给出可直接跑的脚本、收敛曲线对比和参数设置的血泪经验适合做通信基带仿真、毕业设计或者换算法选型的同学照着复现、对比、改参数。2. 理论先行LMS 与 RLS 的迭代公式和收敛特性差异自适应均衡的核心是把均衡器抽头向量 w 看成待优化的变量用误差信号 e(n) 驱动更新。LMS 和 RLS 的区别在于更新方向和步长怎么算。一个用的是瞬时梯度一个用的是递推最小二乘一个只需要几个标量运算一个要维护一个矩阵。这两种不同的更新策略直接决定了它们在收敛速度、稳态误差和复杂度上的表现。2.1 LMS 的最小均方误差迭代μ 是唯一的步长旋钮LMS 对代价函数 J(w)E[|e(n)|^2] 做最陡下降实际实现时用瞬时梯度代替统计梯度更新式写为w(n1) w(n) μ · e(n) · x(n)这里 x(n) 是均衡器输入向量e(n)d(n)-y(n)d(n) 是期望符号y(n) 是均衡器输出。μ 是步长因子它同时决定收敛速度和稳态波动。为了保证均方意义下收敛μ 需要满足 0 μ 2/λ_maxλ_max 是输入自相关矩阵的最大特征值。实际工程中不会去算特征值而是从输入信号二阶矩粗略估计通常取 μ 为 0.0010.05 这个量级。LMS 的优点是每一轮迭代只有一次矩阵乘向量和一次向量乘标量存储量只有抽头个数 M。缺点也很明显当输入信号自相关矩阵的特征值扩展度较大时收敛速度会被拉得很慢。均匀谱的信道还好若信道深度衰落导致频谱出现凹点LMS 要跑几千步才能达到稳态。这个“慢”不是实现问题而是算法本身对输入信号二阶统计量的敏感——这也就是为什么在信道失真严重的场景下人们更愿意考虑 RLS。2.2 RLS 的递推最小二乘遗忘因子 λ 与逆相关矩阵 PRLS 的目标是最小化带指数加权的最小二乘累积误差J(n) Σᵏ₌₁ⁿ λⁿ⁻ᵏ |e(k)|²其中 0 λ ≤ 1λ 称为遗忘因子。当 λ1 时所有历史误差权重相同就是标准最小二乘λ 越小过去误差衰减得越快算法越能跟踪信道变化但也会被噪声牵着走。RLS 的迭代可以写成g(n) P(n-1)·x(n) / (λ xᴴ(n)·P(n-1)·x(n))w(n) w(n-1) g(n)·e(n)P(n) (P(n-1) - g(n)·xᴴ(n)·P(n-1)) / λ这里的 P(n) 是输入自相关矩阵逆的估计g(n) 是增益向量形式上跟卡尔曼滤波类似。由于它直接估计了二阶统计量并用于更新RLS 对输入相关矩阵的特征值扩展不敏感所以即使在严重 ISI 信道下也能快速收敛。代价是每一轮要更新一个 M×M 矩阵计算量约 O(M²)存储量也是 O(M²)。在抽头数 M32 以上时RLS 的乘法次数会比 LMS 多一个量级实时硬件实现时要特别注意资源占用。P 矩阵的初值 δI 也会影响收敛过程。δ 取得大代表初始信任输入自相关逆很大算法前期收敛激进但可能带来较大振荡δ 取太小前期冲得慢。一般实数 BPSK 场景可以取 δ1 或更大的值复杂调制或低信噪比时再根据仿真结果调整。2.3 两者核心参数与代价对比一张表说清选谁对比项LMSRLS每步计算量O(M)O(M²)存储量O(M)O(M²)收敛速度慢受特征值扩展影响快几乎不受特征值扩展影响稳态误差与 μ 正相关与 1-λ 正相关跟踪能力弱需要重新收敛强可通过调 λ 适应时变信道调参敏感度只有一个 μ较直观λ 和 δ 组合调参更微妙数值稳定性稳定高精度要求下 P 矩阵易发散从这张表可以看出LMS 适合信道变化不快、算力受限的场景RLS 适合信道时变、要求快速跟踪的场景。标题里的对比仿真核心就是把这种差异用同一套路数据量化出来让一个静态信道和一个突变信道分别跑两种算法看它们收敛速度差多少稳态误差差多少最后落脚到“什么时候该用哪种算法”。3. 用 MATLAB 2021a 搭建一个最小可跑的对比仿真3.1 仿真模型BPSK 信道、横向均衡器与训练序列我这里用的模型是基带等效收发发送端产生 BPSK 符号1/-1经过一个三抽头 FIR 信道产生 ISI再加高斯白噪声。接收端用一个 M 阶横向均衡器期望信号是已知的训练序列。整个过程是符号间隔采样不考虑定时恢复和过采样。信道我设成h_chan [0.8, 0.3, 0.1]其中 0.8 是主径后面两个是反射路径。这样接收信号rx(n)主要包含了当前符号tx(n)但也混入了前两个符号的能量正好制造了可移除的码间干扰。均衡器输入向量取当前接收采样和它之前的 M-1 个采样即rx(n:-1:n-M1)目标是从这一段观察向量里把tx(n)恢复出来。训练序列用 2000 个 BPSK 符号。前 200 个符号足够两种算法进入收敛区后面一段用于统计误码率。为了让对比公平两种算法共享同一组发送符号、同一组信道增益、同一组噪声样本唯一区别是算法本身。3.2 主脚本完整可运行代码与参数说明下面这个脚本直接放到 MATLAB 2021a 及以上版本即可运行我用rng(2024)固定了随机种子每次运行结果一致方便你调参数时做横向对比。% LMS vs RLS 自适应均衡仿真MATLAB R2021a clear; close all; clc; rng(2024); % 固定随机种子结果可复现 % ---------- 系统参数 ---------- N 2000; % 训练段符号数 M 4; % 均衡器抽头数 mu 0.01; % LMS 步长 lambda 0.98; % RLS 遗忘因子 delta 1; % RLS P 矩阵初始化系数 SNRdB 20; % 信噪比 dB h_chan [0.8, 0.3, 0.1]; % 三抽头基带信道 % ---------- 发送与接收 ---------- tx 2 * randi([0 1], N, 1) - 1; % BPSK: -1 / 1 rx_no_noise filter(h_chan, 1, tx); % 经信道含 ISI signal_power mean(abs(rx_no_noise).^2); noise_power signal_power / 10^(SNRdB/10); noise sqrt(noise_power) * randn(N, 1); rx rx_no_noise noise; % 加噪声后接收信号 % ---------- 初始化 ---------- w_lms zeros(M, 1); % LMS 抽头向量 w_rls zeros(M, 1); % RLS 抽头向量 P delta * eye(M); % RLS 逆相关矩阵初值 e_lms zeros(N, 1); e_rls zeros(N, 1); y_lms zeros(N, 1); y_rls zeros(N, 1); % ---------- 训练循环 ---------- for n M:N x rx(n:-1:n-M1); % 当前接收向量M×1 % ---- LMS ---- y_lms(n) w_lms. * x; e_lms(n) tx(n) - y_lms(n); w_lms w_lms mu * e_lms(n) * x; % ---- RLS ---- y_rls(n) w_rls. * x; e_rls(n) tx(n) - y_rls(n); gain P * x / (lambda x * P * x); w_rls w_rls gain * e_rls(n); P (P - gain * x * P) / lambda; end % ---------- 后段误码率统计最后500个符号 ---------- idx N-499:N; sym_lms sign(real(y_lms(idx))); sym_rls sign(real(y_rls(idx))); ber_lms mean(sym_lms ~ tx(idx)); ber_rls mean(sym_rls ~ tx(idx)); fprintf(LMS 后500符号误码率: %.4f\n, ber_lms); fprintf(RLS 后500符号误码率: %.4f\n, ber_rls); % ---------- 误差平方学习曲线100点滑动平均 ---------- e2_lms e_lms.^2; e2_rls e_rls.^2; win 100; sm_lms movmean(e2_lms, win); sm_rls movmean(e2_rls, win); % ---------- 绘图 ---------- figure; semilogy(1:N, sm_lms, b, LineWidth, 1.2); hold on; semilogy(1:N, sm_rls, r, LineWidth, 1.2); grid on; xlabel(迭代次数 n); ylabel(误差平方滑动平均); legend(LMS, RLS); title(LMS vs RLS 自适应均衡收敛曲线);这段代码的逻辑说明如下tx是发送的 BPSK 符号filter(h_chan,1,tx)得到无噪接收序列rx_no_noise再按信噪比折算噪声功率后叠加噪声。训练循环从nM开始因为输入向量要用到当前及前 M-1 个接收样点前 M-1 个样点无法凑满向量。LMS 部分就是最基本的三个公式输出、误差、更新。RLS 部分则额外维护P矩阵增益向量gain的计算里lambda x*P*x是一个标量保证除法安全。参数说明M4是抽头数对三抽头信道来说足够覆盖 ISI 径设少了均衡能力不足设太多会放大噪声mu0.01是 LMS 步长输入信号功率大约在 0.1 量级这个步长不至于发散也不会太慢lambda0.98是 RLS 遗忘因子表示 100 个符号前的历史误差权重衰减到约 13.5%用于静态信道绰绰有余。delta1是 P 矩阵的初始化加权BPSK 场景下取 1 比较通用。3.3 跑通后的输出收敛曲线与误码率怎么看运行脚本后命令行会打印两个误码率图形窗口会画出两条学习曲线。正常情况下RLS 的红色曲线会比 LMS 的蓝色曲线更快掉下来也就是在更小的迭代次数就降到 10⁻² 量级。LMS 因为步长只有 0.01会经历一个较长的线性收敛区可能要到 300500 次迭代才进入稳态。需要注意的坑是如果只盯这张图很容易被“谁先掉下来”带偏。误码率才是最终目标。我调参时见过 LSM 在图上看起来收敛快但稳态误差高出 RLS 一个数量级最终误码率反而更差。所以第三部分的代码把最后 500 个符号的误码率也打印出来就是提醒自己收敛速度是过程误码率才是结果。4. 核心指标怎么量化收敛速度、稳态误差与抽头系数对比只是画两条曲线说服力不够。要把 LMS 和 RLS 的差异写成数字在论文或项目报告中才能站得住。以下三个指标是我必看的收敛速度、稳态误差、抽头系数轨迹。它们分别对应“多快到位”“到位后抖多大”“走的什么路径”。4.1 收敛速度以误差首次进入阈值所需的迭代次数为准收敛速度的度量不是看曲线“好像平了”而是看误差平方滑动平均第一次低于某个阈值。阈值怎么定我常用绝对阈值比如误差平方 10⁻²对应误差幅度 0.1对 BPSK 来说已经接近可靠判决。如果你想让阈值更自适应也可以取最终稳态误差的 1.1 倍但那样前期的判决会牵扯进来不如下固定阈值直观。在主脚本后面加这几行threshold 1e-2; % 误差平方阈值 first_lms find(sm_lms threshold, 1); first_rls find(sm_rls threshold, 1); fprintf(LMS 首次低于阈值: %d 次迭代\n, first_lms); fprintf(RLS 首次低于阈值: %d 次迭代\n, first_rls);find(..., 1)返回第一个满足条件的索引也就是首次进入稳态的迭代次数。注意这里的sm_lms和sm_rls是滑动平均后的误差平方直接用原始误差平方容易被单个噪声尖刺误导。阈值的选择可以结合信噪比调整信噪比低时稳态误差本身就大阈值要放宽否则可能一次都找不到。4.2 稳态误差取末尾一段误差平方均值别被曲线图骗了稳态误差是收敛之后误差功率的平均反映算法的最终精度。取主脚本里已经算好的e2_lms和e2_rls截最后 500 个点emse_lms mean(e2_lms(end-499:end)); emse_rls mean(e2_rls(end-499:end)); fprintf(LMS 稳态误差(EMSE): %.6f\n, emse_lms); fprintf(RLS 稳态误差(EMSE): %.6f\n, emse_rls);end-499:end是去掉收敛过程只看已稳定区域。稳态误差跟参数高度相关LMS 的 EMSE 大约正比于 μμ 翻倍稳态抖动也接近翻倍RLS 的 EMSE 大约正比于 1-λλ 越接近 1稳态误差越小但跟踪信道变化就越慢。这在参数扫描时能直接看到。另外稳态误差和误码率不是简单线性关系。在 BPSK 下误码率主要由误差概率密度在判决边界附近的积分决定所以单看 EMSE 可能不够还要结合星座图统计错误数。这也是为什么我在主脚本里同时打印了误码率。4.3 抽头系数轨迹看 LMS 和 RLS 的“到达路径”除了误差曲线抽头系数的变化轨迹也很有信息量。它告诉你算法是如何逼近最优解的LMS 往往是一步步爬RLS 则会先大幅跳变再快速修正。要记录轨迹在主脚本循环里加上w_lms_history zeros(N, M); % 在循环前分配 w_rls_history zeros(N, M); % 循环内 LMS 更新后加: w_lms_history(n, :) w_lms.; % 循环内 RLS 更新后加: w_rls_history(n, :) w_rls.;画图figure; plot(1:N, w_lms_history(:, 1:3), b, LineWidth, 1); hold on; plot(1:N, w_rls_history(:, 1:3), r, LineWidth, 1); xlabel(迭代次数); ylabel(抽头系数); title(抽头系数收敛轨迹蓝LMS 红RLS);可以看到几组抽头迅速从 0 逼近最优解。RLS 的轨迹通常在前 50 次迭代就有较大幅度变化而 LMS 的轨迹则平缓许多。需要注意的是如果抽头系数在收敛后持续缓慢漂移说明稳态噪声在持续更新权重这也是 LMS 稳态误差偏大的直接表现。5. 仿真中的五个高频坑从翻车到稳定复现5.1 现象LMS 误差不降反升我见过不少同学把mu直接设成 0.1结果误差曲线像过山车越跑越大。原因是步长超过了收敛边界。输入向量 x 的功率大约是几个信道的能量之和0.1 在低功率时看似安全但遇到噪声峰值会瞬间破坏权重。解决办法是把步长改成对输入功率归一化的形式也就是用 NLMSmu_eff mu / (x * x 1e-6); % 归一化步长 w_lms w_lms mu_eff * e_lms(n) * x;x*x是输入向量能量的瞬时估计加 1e-6 防止除零。这样即使输入幅度突然增大有效步长也自动缩小。NLMS 的收敛速度对输入功率不敏感调参比 LMS 好调。5.2 现象RLS 的 P 矩阵很快变成 NaN/InfRLS 在高信噪比或信道条件较好时P 矩阵数值容易溢出。原因是P (P - gain*x*P)/lambda每次都要做矩阵乘法反复迭代后舍入误差累积λ 越接近 1P 越容易失去正定性最终出现 Inf。解决思路之一是加对角加载P (P - gain * x * P) / lambda 1e-6 * eye(M);每次更新后在 P 的对角线上加一个极小常数等价于给最小二乘问题加正则项能有效阻止 P 病态发散。另一种办法是改用数据域直接传递 RLS 算法如 QR-RLS但代码复杂度高仿真阶段加对角加载已经够用。5.3 现象训练序列与均衡输出对不齐误差始终很大均衡器输入向量用的是rx(n:-1:n-M1)期望用的是tx(n)。如果信道传播导致主径延迟不是 0比如信道抽头第一位不是主径那么rx(n)对应的主要符号其实是tx(n-d)d 为正延迟。此时用tx(n)做期望误差里永远混着一个符号延迟算法再怎么调都收不到接近零。解决办法是把期望信号也做延迟d 1; % 需要根据信道主径位置估计 tx_delayed [zeros(d,1); tx(1:end-d)]; e tx_delayed(n) - y(n);多数信道模型的第一个抽头是主径时不需延迟调整但遇到信道首径很小、次径很大的场景记得检查max(abs(h_chan))的下标位置。5.4 现象随机种子一换算法优劣排序就变单次实验里 LMS 可能因为噪声峰值少而显得比 RLS 还好但换个rng种子结论就反转。自适应均衡的收敛过程是随机的不能拿一次曲线定结论。我做对比至少跑 50 次独立实验每次重新生成噪声对随机信道场景也可以每次重生成信道然后对收敛曲线和稳态误差取平均for trial 1:50 rng(trial); % 重新生成噪声和信道可选 % 运行整个训练循环 emse_lms_all(trial) emse_lms; emse_rls_all(trial) emse_rls; end mean_emse_lms mean(emse_lms_all); mean_emse_rls mean(emse_rls_all);固定rng(trial)是为了保证试验序列可复现50 次平均后的曲线会平滑很多算法优劣势一目了然。5.5 现象R2021a 里运行报错函数版本不一致主脚本里用了movmean这个函数在 R2016a 开始就有正常 R2021a 都能跑。如果遇到旧代码是filter(ones(1,win)/win, 1, e2)结果会有一点相位延迟但画学习曲线无伤大雅。更常见的问题是x与x.混用。在实数 BPSK 场景两者一样但一旦以后换成 QPSK复数信号必须用做共轭转置不能省。另外randi在 R2021a 没问题老版本 R2011 之前的randint才需要改。总之版本问题优先看函数名是否在“函数浏览器”里能查到查不到就换成替代写法。6. 从仿真到验证信道突变对比与参数调优技巧静态信道收敛只是第一步实际信道的多径结构是随时间变化的。要验证两种算法的跟踪能力我建议在训练序列中段让信道突变比如第 1000 个符号处把h_chan从[0.8, 0.3, 0.1]换成[0.5, 0.6, -0.2]。注意不能简单在循环里切换滤波器系数因为rx是提前离线生成的要做分段滤波再把两段拼接tx1 tx(1:999); tx2 tx(1000:end); rx1 filter([0.8 0.3 0.1], 1, tx1); rx2 filter([0.5 0.6 -0.2], 1, tx2); rx [rx1; rx2];这样前 999 个符号走旧信道后面走新信道。跑完看误差曲线RLS 依靠 P 矩阵记忆突变初期可能因为旧信道统计信息残留而剧烈跳动但几轮后就能重新收敛LMS 没有历史累计结构反而可能更快摆脱旧信道的影响。这个对比能很明显地展现出 RLS“跟踪快但代价大”的实质。参数调优的关键是扫描。我常见做法是让 mu 在[0.005 0.01 0.02 0.05]里扫lambda 在[0.9 0.95 0.98 0.99]里扫每组参数跑 50 次取平均mus [0.005 0.01 0.02 0.05]; lambdas [0.9 0.95 0.98 0.99]; for i 1:length(mus) for j 1:length(lambdas) mu mus(i); lambda lambdas(j); % 运行完整仿真记录 emse_lms 和 emse_rls end end扫描结果通常会形成一张表mu 越大LMS 收敛越快但稳态误差越大lambda 越大RLS 稳态误差越小但重新收敛越慢。这个表最终会告诉你在目标信道下到底牺牲哪一边更划算。我自己的教训是一开始只看静态信道曲线觉得 mu0.02 收敛快、误码率也低就定了这个参数。后来做信道突变实验发现 LMS 在这个参数下重新收敛要 600 多个符号而 RLS 只用 50 多个符号。从此以后我固定地把“静态稳态误差、突变重收敛时间、后段误码率”三个指标一起列参数从表格里选不再靠曲线图拍脑袋。这个习惯帮我躲过了不少论文数据翻车的坑希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?