ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MFAC无模型自适应控制:CFDL/PFDL/FFDL动态线性化与Matlab仿真

MFAC无模型自适应控制:CFDL/PFDL/FFDL动态线性化与Matlab仿真 MFACModel Free Adaptive Control无模型自适应控制在数据驱动控制领域里算是名声大但门槛也微妙的一支。说它门槛微妙是因为理论公式看着不多但要真正把CFDL、PFDL、FFDL三种动态线性化方法各自跑通、还能讲明白它们之间差在哪不亲手在Matlab里仿真几轮很难有感觉。我最近把这三套结构在几个不同非线性系统上完整复现了一遍从公式推导到程序调参都踩了不少坑这篇就把整个流程、关键代码和排查经验整理出来给正在做这个方向或者想拿MFAC当基线算法做对比的朋友做个参考。1. MFAC整体设计思路不建模怎么控住非线性系统1.1 为什么传统控制在这个场景里别扭做控制的人通常习惯先建模再设计控制器。经典PID虽然不依赖精确模型但调参本质还是在“猜”对象特性而且对被控对象的非线性变化、时变特性响应很被动。现代控制理论里的状态空间方法性能上限高可它要求先拿到足够准确的状态方程这对很多实际被控对象来说本身就是一件奢侈的事情——化工过程、电机驱动、气动系统想精确建模需要花大量时间做机理分析和参数辨识模型出来之后还经常因为工况变化而失效。MFAC走的是另一条路直接利用系统的输入输出数据在每个采样时刻把非线性系统“局部拉直”也就是做动态线性化然后基于这个局部线性模型设计控制器。它不依赖全局数学模型而是靠在线估计一个叫作“伪偏导数”或“伪梯度”的量把系统当前工作点附近的输入输出关系近似成一组简单的线性关系。用生活里的例子类比这就像你不需要完整掌握一整条山路的路况只需要盯着眼前几米的路面来判断方向盘该打多少边走边修正。具体到实现MFAC里有三套经典的动态线性化方案就是标题里提到的CFDL、PFDL和FFDL。它们在数学形式上层层递进背后的适用场景也各不相同。1.2 三种动态线性化的形式与定位CFDL紧凑格式动态线性化是最基础的一档。它在每个时刻假设系统可以写成Δy(k1) φ_c(k) · Δu(k)这里的φ_c(k)就是伪偏导数PPD是一个标量。Δu(k)表示当前时刻输入相对于上一时刻的变化量。它的核心假设是输出变化主要是由最新的输入变化引起的过去的历史信息都折叠到这个标量φ_c(k)里了。这种形式最简单辨识参数只有一个适合SISO系统做快速验证。PFDL偏格式动态线性化把φ扩展成了一个向量Δy(k1) φ_p(k)^T · [Δu(k), Δu(k-1), ..., Δu(k-L1)]也就是说系统输出变化不仅和最新的输入变化有关还和过去L个时刻的输入变化有关。这相当于承认系统的动态响应是有“惯性”的不能只看一拍输入。代价是需要同时在线估计L个参数。FFDL全格式动态线性化更进一步把输出历史变化也纳入进来Δy(k1) φ_f(k)^T · [Δu(k), ..., Δu(k-Lu1), Δy(k), ..., Δy(k-Ly1)]这意味着它认可系统可能本身带有较强的输出反馈特性比如振荡、迟滞、惯性。从参数数量上看FFDL需要估计LuLy个参数结构最复杂但对系统的表征能力也最强。三者的包含关系很好记CFDL是PFDL在L1时的特例PFDL又是FFDL在Ly0时的特例。选择哪一档本质是在“模型复杂度”和“系统动态复杂度”之间做权衡。方法线性化形式待估参数典型适用对象CFDLΔy(k1) φ_c(k)·Δu(k)1个标量动态相对简单的SISO系统PFDLΔy(k1) φ_p(k)^T·ΔU_L(k)L维向量输入动态惯性明显的系统FFDLΔy(k1) φ_f(k)^T·ΔH(k)LuLy维向量输出反馈、振荡、时滞系统1.3 我的选型建议复现完三套结构之后我的体会是不要盲目上FFDL。参数越多在线估计的压力越大对噪声和初始值也越敏感。如果被控对象本身动态不复杂CFDL就能达到相当好的跟踪效果上FFDL反而容易把伪梯度的某些分量估计到不合理的数值带来控制量抖动。我自己的选择逻辑是这样第一次接触MFAC先用CFDL把整个闭环框架跑通把估计律、控制律、重置机制这几个环节都搞明白再逐步扩展PFDL和FFDL。这样做的好处是出问题的时候你知道大概率是哪个环节的事而不是在一个大杂烩里抓瞎。下面我就把每个环节的原理和实现细节拆开讲。2. 关键公式逐个拆解估计律、重置机制、控制律2.1 伪梯度到底在估计什么MFAC里最核心的东西就是伪偏导数φ的在线估计。它的思路可以理解成一个带约束的在线最小二乘问题我们希望找到的φ(k)要能尽量好地解释最近那一拍输入输出变化量之间的关系同时不希望它和上一次的估计值差得太远。对应到数学上是这样一个准则函数J (Δy(k) - φ(k-1)·Δu(k-1))² μ·(φ(k) - φ(k-1))²第一项衡量的是“上一拍预测误差”第二项是“估计值变化惩罚”μ是惩罚因子。对这个函数关于φ(k)求极值解出来就是常用的投影估计律φ(k) φ(k-1) η·Δu(k-1) / (μ Δu(k-1)²) · (Δy(k) - φ(k-1)·Δu(k-1))这里的η是估计步长控制每拍修正的幅度。你仔细看这个式子就会发现它和最小均方LMS类的自适应滤波算法在结构上是一致的误差项乘以输入项再除以正则分母然后叠加到旧估计上。μ加在分母上的作用尤其关键它不只是防止分母为零更是在输入变化量Δu很小的时候避免估计值被一个微小的数据变化带飞。实际调试中μ这个参数最容易被忽略。我第一次跑仿真时μ取0.001结果φ曲线在初始阶段直接冲向几十然后控制量剧烈震荡。把μ调到1之后整个系统立刻平稳下来。所以这里先立一个经验μ不能太小它本质上是在给伪偏导数估计做“阻尼”。2.2 重置机制防止伪偏导数跑飞的保险丝纯靠估计律在线更新φ是有风险的。因为估计只是一个数值迭代过程它没有物理意义约束可能出现两种情况一是φ的绝对值小到接近零这时候控制律分母里的λφ²会非常接近λ控制器输出容易失去调节能力二是φ的符号发生突变本来应该是正的“增益”突然变成负的控制律的方向就会反掉系统直接发散。所以MFAC里几乎都会配一个重置机制其实就是几个if判断条件φ(k) φ0如果 φ(k) ≤ ε 或者 φ(k)的符号与初始值不同这个重置动作相当于给伪偏导数上一个“保险丝”。我自己的理解是它把在线辨识的搜索空间限制在一个合理区间里避免估计值漂移到物理上无意义的方向。ε一般取1e-5到1e-3这个数量级太小会让重置形同虚设太大则会让估计频繁归位导致系统失去对参数变化的适应能力。2.3 控制器怎么设计出来控制器的设计准则也是一个优化问题。我们希望系统输出y(k1)尽量接近参考值y*(k1)同时又不想控制量u(k)比上一拍变化得太猛。于是准则函数写成J (y*(k1) - y(k1))² λ·(u(k) - u(k-1))²λ是控制增量的惩罚因子。把动态线性化模型y(k1) y(k) φ(k)·Δu(k)代入再对u(k)求极值就得到控制律u(k) u(k-1) ρ·φ(k) / (λ φ(k)²) · (y*(k1) - y(k))这里多了一个ρ步长因子它在理论推导里可以合并进λ但实践中分开调会更灵活相当于在控制器输出上设置一个缩放系数。很多人不理解为什么分母里要有λφ²这一项直接除以φ不就行了从数值角度看这一项就是一个正则化当φ很小甚至接近零时控制器增益不会被无限放大。从控制效果角度看λ越大控制增量被压得越狠系统响应越慢λ越小控制动作越激进跟踪越快但越容易振荡。这个平衡是MFAC调参里最核心的手感。3. Matlab代码实现主循环、三个方法与调参3.1 程序整体结构我用纯m脚本做的仿真没有上Simulink因为这种非线性系统对比实验脚本式仿真的可读性和可复现性都更好。整个项目文件组织大概是这样mfac_study/ init_params.m # 所有公共参数集中管理 main_cfdl.m # CFDL主程序 main_pfdl.m # PFDL主程序 main_ffdl.m # FFDL主程序 plant_1.m # 被控对象1非仿射非线性 plant_2.m # 被控对象2带输出历史影响 plant_3.m # 被控对象3带随机扰动 plot_results.m # 统一绘图把所有参数集中放在init_params里是我后来加上的习惯。最初版本每个主程序里各自写参数结果对比实验时改一个参数要同步改三个文件非常容易漏。集中管理之后改参数只需要动一处跑完三个main再统一对比。3.2 CFDL完整主循环CFDL的完整主循环是理解整个MFAC框架的钥匙。我先贴最核心的代码段再逐行解释逻辑。clear; clc; N 1000; % 参考轨迹前半段方波后半段正弦 yd [0.5 * ones(1, N/2), 0.5 0.4*sin(0.02*(1:N/2))]; % 公共参数 mu 1; % PPD估计惩罚因子 eta 0.8; % PPD估计步长 rho 0.6; % 控制律步长 lambda 1.2; % 控制增量惩罚 eps 1e-4; % PPD重置阈值 phi0 1; % PPD初值 % 初始化 y zeros(1, N); u zeros(1, N); y(1) 0; u(1) 0.05; phi_hat phi0; for k 2:N % 用最近的历史变化量更新PPD dU_last u(k-1) - u(k-2); % Δu(k-1) dY_now y(k) - y(k-1); % Δy(k) if k 2 phi_hat phi0; else phi_hat phi_hat eta * dU_last / (mu dU_last^2) * (dY_now - phi_hat * dU_last); end % 重置机制 if abs(phi_hat) eps || sign(phi_hat) ~ sign(phi0) phi_hat phi0; end % 控制律 u(k) u(k-1) rho * phi_hat / (lambda phi_hat^2) * (yd(k) - y(k-1)); % 被控对象u(k)施加后产生y(k) y(k) plant_1(y(k-1), u(k)); end注意这个写法里我让u(k)先算出来再喂给被控对象算出y(k)形成闭环。和纯公式推导有一点索引上的错位但物理顺序是对的当前拍的控制量作用于对象得到当前拍输出供下一拍估计PPD使用。初值u(1)设成一个很小的正数而不是0是为了避免初始时刻Δu为零导致估计律分母退化。3.3 从CFDL扩展到PFDL和FFDL的向量化要点PFDL相对CFDL的主要改动是把标量φ换成L维向量把单个Δu换成滑动窗口里的L个输入变化量。核心代码段长这样L 3; phi_vec ones(L, 1); % 伪梯度向量初值 u_win zeros(L, 1); % 输入变化量滑窗 for k 2:N dU_iter u(k-1) - u(k-2); u_win [dU_iter; u_win(1:end-1)]; % 滑窗更新最新的放最前面 dY_now y(k) - y(k-1); phi_vec phi_vec eta * u_win / (mu u_win * u_win) * (dY_now - phi_vec * u_win); if norm(phi_vec) eps phi_vec ones(L, 1); end u(k) u(k-1) rho / (lambda phi_vec * phi_vec) * phi_vec * (yd(k) - y(k-1)); y(k) plant_2(y(k-1), u(k)); end这里要注意的是分母里的u_win * u_win它是对整个滑窗向量做内积相当于L维情况下μ的对比项。滑窗的更新方式在Matlab里就是一行但换成别的语言时要注意用循环或者队列结构实现不然每拍都重新拼接数组效率很差。FFDL又比PFDL多维护一个输出变化量的滑窗。它的伪梯度向量同时包含输入分块和输出分块Lu 2; Ly 2; % 输入、输出滑窗长度 phi_f ones(LuLy, 1); u_win zeros(Lu, 1); y_win zeros(Ly, 1); for k 2:N dU_iter u(k-1) - u(k-2); dY_iter y(k) - y(k-1); u_win [dU_iter; u_win(1:end-1)]; y_win [dY_iter; y_win(1:end-1)]; H [u_win; y_win]; % 全格式信息向量 phi_f phi_f eta * H / (mu H * H) * (dY_now - phi_f * H); if norm(phi_f) eps phi_f ones(LuLy, 1); end u(k) u(k-1) rho / (lambda phi_f * phi_f) * phi_f * (yd(k) - y(k-1)); y(k) plant_3(y(k-1), u(k)); end写FFDL时最容易犯的错是滑窗里数据顺序不一致。比如PFDL里最新输入变化量放在向量最前面那FFDL的输出变化量滑窗也要遵循同样顺序不然伪梯度向量各分量的含义就对不上。我建议在代码注释里明确标注每个位置对应的时间戳哪怕多写几行注释也比事后排查逻辑错误省时间。3.4 参数整定的先后顺序MFAC参数虽然看着只有μ、η、ρ、λ、ε、φ0这几个但乱调的后果就是系统发散得毫无头绪。我反复试了几轮之后总结出一套比较稳的调整顺序第一步固定μ和η先调控制律里的ρ和λ让系统在参考轨迹变化比较平缓的区段稳定下来目标是“能跟得上、不发抖”。第二步再回头微调μ和η改善PPD估计的动态响应目标是让跟踪误差更小、过渡更平滑。第三步最后处理ε和φ0这两个参数主要影响初始阶段和异常情况下的表现对稳态性能影响相对较小。这样做的好处是每个参数控制的是不同的时间尺度ρ和λ直接影响控制量动作对应闭环响应速度μ和η影响PPD估计对应参数辨识的平滑度ε和φ0更像边界条件只在特殊时刻起作用。按时间尺度分开调问题定位会清晰很多。4. 仿真实验三个非线性系统上的复现结果4.1 实验对象与参考轨迹我选了三个差异比较明显的离散时间非线性系统来做对比编号系统方程设计意图系统1y(k1) 0.6·y(k)/(1 y(k)^2) 1.2·u(k)^3非仿射非线性关系基础场景系统2y(k1) 0.8·y(k)·sin(y(k-1)) 0.5·u(k) 0.1·u(k)^2输出历史自激励明显系统3y(k1) 0.9·y(k) 0.1·u(k) 0.2·randn线性主导但有随机干扰参考轨迹我设的是前半段方波、后半段正弦。方波能考验系统的阶跃响应和超调控制能力正弦能考验持续跟踪能力两个放在一起跑一轮基本就能看出控制器在快变和慢变两种工况下的表现。4.2 三组实验记录系统1上CFDL的表现就相当好。φ初值取1ρ取0.6λ取1.2跟踪方波时超调很小正弦段最大误差不到0.05基本没有稳态误差。PFDL在系统1上提升不大这符合预期——系统本来的动态主要由当前输入决定历史输入变化量提供的信息有限。系统2因为方程里明显带着y(k-1)的非线性项输出动态的历史影响很强。CFDL在这里开始露怯跟踪方波时出现了一些低频振荡原因是只用一个标量PPD去解释输出变化信息量不够。PFDL把窗口L加到3之后振荡明显被压下去。FFDL在系统2上的效果和PFDL接近但伪梯度向量里增加了输出历史维度后估计波动更平稳。系统3有随机扰动三种方法都能跟上参考轨迹但控制量噪声水平差异明显。CFDL因为结构最简对扰动的响应最直接控制动作比较毛糙FFDL因为伪梯度信息更丰富估计环节相当于对扰动信息做了一次平滑控制量明显更干净。这个现象值得注意FFDL不只是为复杂动态准备的它对噪声工况也有天然的滤波效果。4.3 结果怎么解读我把三组实验的性能指标整理成一张表方便对比场景方法跟踪RMSE控制量变化幅度系统1CFDL0.02量级小平稳系统2CFDL0.08量级有振荡中等有摆动系统2PFDL0.04量级中等平稳系统2FFDL0.04量级中等平稳系统3CFDL0.06量级毛糙明显系统3FFDL0.05量级明显平滑这些数字是我某个具体参数配置下的一次实验记录你换参数肯定会不一样但相对趋势是稳定的结构越复杂在动态复杂场景下的表现越好结构简单的在常规场景下也不吃亏。看结果时不要只看跟踪误差一定要配合伪偏导数曲线一起看。CFDL的PPD曲线在系统1上会稳定在一个正值附近小幅波动说明估计有意义如果PPD曲线长期贴着重置阈值反复触发说明系统在当前结构下根本没法用一个标量描述这时候才需要换PFDL或FFDL。PPD曲线实际上是系统动态复杂度的“照妖镜”。5. 常见问题与参数速查表5.1 发散的三种典型原因我调试过程中遇到的发散问题几乎都可以归到下面三类第一种PPD估计发散。现象是φ曲线冲上天或者持续负值控制量跟着剧烈跳动。原因通常是μ取太小、η取太大导致估计环节对数据噪声过度敏感。解决办法是先增大μ到1左右η降到0.5以下观察φ曲线是否收敛。第二种控制量发散但PPD正常。这种问题出在控制律参数上典型是λ太小、ρ太大。此时φ曲线平稳但u(k)每拍都增加很大的增量系统输出像脱缰野马。解决办法是增大λ减小ρ让控制动作变温和。第三种初始段剧烈波动然后发散。这是初值问题通常和φ0取太大、u(0)取零有关。初始阶段系统还没建立起正确的输入输出关系给一个过大的φ初值会放大第一拍的控制量。我习惯把φ0设在0.5到1之间u初始值设一个小正数而不是严格的零。5.2 参数速查表下面这张表是我在Matlab里复现时常用的参数范围和调整方向供你参考参数含义常见范围调大影响调小影响μPPD估计惩罚0.1 ~ 10估计变平滑响应变慢估计敏感易发散ηPPD估计步长0.1 ~ 1估计收敛快易振荡收敛慢适应差ρ控制律步长0.1 ~ 1跟踪快易超调跟踪慢系统迟钝λ控制增量惩罚0.1 ~ 10控制保守响应慢控制激进易振荡ε重置阈值1e-5 ~ 1e-3重置频繁适应差重置失效易漂移φ0PPD初值0.5 ~ 2初始段冲击大初始段响应慢5.3 调试中值得留意的细节有三个细节是我复现时觉得最坑、但又特别有用的地方。一个是参考轨迹的幅度。很多MFAC公式推导里默认参考输入在合理范围内但实际仿真里如果y*从0跳到1.5而初始PPD还停留在1附近控制增益会在一开始就被放大很多倍造成第一拍控制量爆炸。我建议先把参考轨迹幅度控制在0.5以内调通再逐步放开这样定位问题会快很多。第二个是绘图分析。不要只画y和yd的跟踪曲线一定要把φ曲线、u曲线、Δu曲线全部画出来放在同一张图里。很多时候跟踪误差大不是控制器不行而是PPD估计根本没跟上系统变化。有一次我发现跟踪误差始终压不下去看了φ曲线才知道它已经卡在重置值附近很久了说明当前结构并不适合那个系统。第三个是实验的可重复性。系统3带了randn随机扰动如果不固定随机种子两次实验的RMSE差10%都很正常。做对比实验时务必在程序开头加rng固定种子否则你很难判断性能差异到底是算法导致的还是随机噪声导致的。这个问题我在早期复现时吃过亏拿着两次随机种子不同跑出来的结果对比了大半天结论毫无意义。6. 写在最后这套代码还能怎么扩展复现完CFDL、PFDL、FFDL之后我最大的体会是MFAC看起来结构朴素但它的扩展空间比想象中大得多。伪偏导数这套在线估计框架本质上是把“系统辨识”和“控制”融合在了同一个时间尺度里所以它几乎可以平滑地迁移到各种控制场景。我目前已经开始在原先的框架上做两个方向的改动。第一个是改成MISO系统也就是多输入单输出这时候伪梯度从向量变成矩阵估计律和控制律都要跟着升维代码里需要动的地方主要集中在数据结构和控制器增益计算。第二个是把参考轨迹换成动态变化的时变阶跃测试系统对工作点大幅迁移的适应能力——这也是无模型控制相对传统模型控制最有说服力的场景。如果你准备拿这个复现结果当基线算法我建议你把三套方法的代码保持在同一套框架里只改线性化模块。这样后续无论是加扰动、改非线性对象、还是和其他算法做对比都能用最少的时间成本产出最干净的结果。
返回列表