首页 / 资讯中心 / 文章详情

MFAC无模型自适应控制Matlab复现:CFDL、PFDL、FFDL动态线性化全解析

MFAC无模型自适应控制Matlab复现:CFDL、PFDL、FFDL动态线性化全解析 ★ FEATURED ARTICLE
先给结论MFAC这套东西名字听起来唬人但拆成CFDL、PFDL、FFDL三种动态线性化之后Matlab复现的难度其实不大真正费心思的是把“线性化格式怎么选、伪偏导怎么估、参数怎么整定”这三件事想明白。这篇文章是我自己完整跑通三个非线性系统动态线性研究的复现记录代码逻辑、参数依据、调试过程中踩过的坑都会写出来给准备入坑或者正在跟这个课题死磕的同学做参考。这篇文章适合几类人一是自动化和智能控制方向做课程项目、毕业设计的学生需要把MFAC从公式变成能跑的仿真二是做数据驱动控制方向的研究生想快速对比CFDL、PFDL、FFDL三种方案的区别三是工程师想评估无模型自适应控制能不能用在现场工况不明、数学模型难以建立的系统上。内容侧重点是“用Matlab实现”所以不会堆一堆数学证明而是尽量把每个公式和每段代码对应上。1. MFAC到底在解决什么问题1.1 无模型不是没有模型是不用精确模型MFAC全称Model-Free Adaptive Control中文叫无模型自适应控制。第一次接触它的人通常会被“无模型”三个字带偏以为是完全不需要模型。实际上它不是没有模型而是不用传统的机理建模和系统辨识只依据被控系统的实时输入输出数据在线构造出一个等价的线性模型来设计控制器。打个比方传统控制做系统设计就像你修一台发动机必须知道缸内燃烧过程、曲轴受力、热力学循环才能写出一个精确的数学模型。而MFAC的思路更像一位老司机开车你不用知道发动机内部发生了什么只需要盯着方向盘、油门、车速这些外部变量根据“踩油门之后车速怎么变”这一点反馈就能逐渐学会把车开稳。控制器一开始可能很笨但伪偏导数把输入和输出之间的局部动态关系实时估计出来系统也就慢慢被驯服了。所以MFAC的真正卖点是对受控系统本身的动态特性要求很低不要求知道系统的阶次、结构、参数甚至不要求系统是线性的只要输入输出数据可用控制设计就能往前走。这也是它在一堆现代控制方法里显得特别接地气的原因。1.2 动态线性化把非线性变成在线仿射系统MFAC的核心思想叫动态线性化。常见的非线性离散系统可以写成y(k1) f(y(k), y(k-1), ..., u(k), u(k-1), ...)这个f通常没法直接用来控制。动态线性化做的事情是在每一个工作时刻k不去找整个非线性函数的全局解析式只在一个局部“窗口”内用一个线性时变模型来近似输入增量到输出增量的因果关系也就是Δy(k1) Φ(k)^T ΔU(k)其中Δy(k1)是输出增量ΔU(k)是由输入增量组成的向量Φ(k)是伪偏导数或者伪梯度向量。这个线性模型不是通过机理推导出来的而是根据输入输出数据在线估计出来的所以每个时刻都可能不同。控制器就在这个当前时刻的线性模型上做设计输出控制量然后下一时刻再刷新线性模型形成闭环。CFDL、PFDL、FFDL这三个名字本质上就是选择ΔU(k)里面放哪些历史信息。输入增量只取当前一步叫紧格式把过去若干步的输入增量都放进去叫偏格式把过去若干步的输入和输出增量一起放进去叫全格式。它们的共同点是都基于输入输出数据在线估计区别只是“动态线性化”的窗口开得多大。1.3 为什么动态线性化适合非线性系统很多人会问如果系统本身很强非线性一个局部线性模型能顶得住吗能但前提是“局部”真的够局部。MFAC不需要在全局范围内让线性模型成立它只需要在当前工作点附近成立。类似你观察一条弯弯曲曲的路远处看是条曲线但你把眼睛贴近地面每一小段路几乎都可以当成直线。MFAC就是贴着地面看路的控制方法。实际工程系统在稳定工况附近工作时输入输出关系往往可以近似看成平滑的动态线性化在这种条件下会很有效。如果系统工作点剧烈跳变或者本身带有严重的纯延迟、非最小相位特性那就不能指望CFDL这种最简形式了需要PFDL或者FFDL把更多的输入输出历史信息引入线性化模型这也是三种格式存在的意义。2. 三种动态线性化方法CFDL、PFDL、FFDL2.1 紧格式CFDL单步输入增量最简洁的入口CFDL的全称是Compact Form Dynamic Linearization紧格式动态线性化。它做的假设很直接Δy(k1) φ_c(k) · Δu(k)其中φ_c(k)是伪偏导数。整个系统输入输出的动态关系仅仅通过当前这一步的控制增量Δu(k)来体现。好处是结构极其简单可调参数少Matlab实现起来就是一个标量循环。CFDL能用的前提是系统的“输入影响输出”这一条路径比较直接。可以理解为系统相对阶低没有明显的输入延迟当前控制量变化对下一拍输出变化的影响占主导地位。如果被控对象带有明显的大迟延或者输出对未来好几步输入的历史都很敏感那CFDL的动态线性化精度就不够用了。复现时我建议第一个系统一定用CFDL来跑因为它的逻辑最清晰。伪偏导估计、控制律、重置机制三块代码加起来不超过二十行一旦跑通后面PFDL和FFDL就是在它基础上做扩展心理压力和调试难度都会小很多。2.2 偏格式PFDL引入输入历史窗口PFDL的全称是Partial Form Dynamic Linearization偏格式动态线性化。它在ΔU(k)中不只看当前一步输入增量而是把过去L步输入增量都装进向量ΔU_L(k) [Δu(k), Δu(k-1), ..., Δu(k-L1)]^T对应的动态线性化模型为Δy(k1) φ_p(k)^T ΔU_L(k)这里φ_p(k)是一个长度为L的伪梯度向量。你可以把PFDL理解为CFDL的“加内存”版本既然某一个时刻的输入变化不一定会立刻完全体现在输出上那我干脆把前几步的输入增量都留下让线性化模型自己决定哪些历史信息更重要。这个方法对付输入延迟和相对阶较高的系统很管用。比如系统里明显带着u(k-1)、u(k-2)这样的滞后项你用CFDL会发现伪偏导估计不停地跳控制效果很差换成PFDL把窗口长度L设成3到5估计出来的伪梯度向量会把历史输入的影响吸收进去闭环响应会平滑很多。但是L不是越大越好。窗口越长伪梯度向量维数越高需要在线估计的参数就越多噪声引起的扰动也会被放大。我调试下来单输入单输出系统L取3到5是性价比比较高的区间再往上提一般不会带来明显改善反而会让估计曲线变得毛糙。2.3 全格式FFDL把输出历史也用起来FFDL的全称是Full Form Dynamic Linearization全格式动态线性化。它在PFDL的基础上又加上了输出增量历史线性化模型写成Δy(k1) φ_u(k)^T ΔU_Lu(k) φ_y(k)^T ΔY_Ly(k)其中ΔU_Lu是Lu步输入增量组成的向量ΔY_Ly是Ly步输出增量组成的向量整个伪梯度向量是这两部分的拼接。FFDL本质上是一个超集如果Ly0只保留Lu步输入增量它就退化成PFDL如果Lu1且Ly0它就退化成CFDL。为什么要把输出历史也收进来因为有些系统的输出动态本身有很强的“惯性”前几拍的输出会对下一拍输出有显著影响。这类系统只用输入增量做线性化模型误差会比较大把输出增量加入回归向量之后伪梯度估计器能更准确地补偿系统的内部动态。代价是参数数量明显增加。假设Lu3、Ly2伪梯度向量一共有5个分量每一个都需要初始化、在线更新、重置保护。如果初始值和参数整定不合理整个估计器很容易发散。所以FFDL适合系统动态比较复杂、且你对MFAC框架已经比较熟悉之后再上不建议作为第一个复现对象。2.4 三种格式的对比与选型思路对比项CFDLPFDLFFDL线性化使用的信息当前一步输入增量过去Lu步输入增量Lu步输入增量Ly步输出增量伪偏导参数数量1LuLuLy实现复杂度低中高对输入延迟的适应能力弱较强强对输出动态惯性的适应能力弱一般强典型适用场景相对阶低、采样周期小的简单非线性系统带输入延迟或高阶动态的系统输出惯性大、输入输出耦合复杂的系统选型时我的习惯是先看控制系统有没有明显输入延迟。没有优先CFDL有一点用PFDL既有输入延迟又有输出惯性或者CFDL怎么调都抖、都发散的再上FFDL。复现这个课题时三个系统正好对应三种格式代码不要混着用每个系统单独维护一套参数这样问题定位会清晰得多。3. Matlab代码复现方案3.1 三个非线性被控对象怎么构造复现MFAC需要一个合适的“试验台”。我选了三个离散非线性系统分别突出三种动态线性化方法的侧重点。系统1给CFDL用y(k1) y(k) / (1 y(k)^2) u(k)^3输出方程里只有当前输出y(k)和当前输入u(k)没有输入延迟没有输出高阶历史是一个典型的“短记忆”非线性系统用CFDL动态线性化足够。系统2给PFDL用y(k1) 0.5 * y(k) * y(k-1) / (1 y(k)^2) u(k) 0.3 * u(k-1)这个系统输出依赖前两拍输出控制输入包含当前步和前一拍u(k-1)带有明显的输入滞后特征。只用CFDL会在u(k-1)那部分插入不了模型导致伪偏导估计震荡用PFDL把输入历史窗口打开之后效果会立刻改善。系统3给FFDL用y(k1) y(k) / (1 y(k)^2) 0.4 * y(k-1) / (1 y(k-1)^2) u(k) 0.2 * u(k-1)^2 0.3 * u(k-2)这个系统的输出历史项占比更重输入侧也带两拍延迟输入输出耦合更复杂。FFDL同时引入输入和输出增量历史才有足够的信息去逼近这种动态。三个系统都是纯仿真对象不会涉及额外依赖Matlab里直接写匿名函数或者单独的函数文件都行。仿真时统一设初始条件y(1)y(2)0u(1)u(2)0总仿真步数N1000参考轨迹yd(k)设成方波信号或者正弦信号都可以。方波信号对控制器的跟踪能力和抗扰动能力考验更大我复现时用的是周期方波每个周期200步。3.2 伪偏导估计器与控制器核心实现以CFDL为例控制器需要两个核心公式。第一个是伪偏导估计φ(k) φ(k-1) η·Δu(k-1)·(Δy(k) - φ(k-1)·Δu(k-1)) / (μ Δu(k-1)^2)第二个是控制律u(k) u(k-1) ρ·φ(k)·(yd(k1) - y(k)) / (λ φ(k)^2)这两个公式在Matlab中翻译成代码很直接。下面是一个可以在循环中使用的核心片段% CFDL无模型自适应控制核心循环片段 dy y(k) - y(k-1); % Δy(k) du u(k-1) - u(k-2); % Δu(k-1) phi(k) phi(k-1) eta * du / (mu du^2) * (dy - phi(k-1) * du); if abs(phi(k)) eps1 || abs(du) eps1 phi(k) phi0; % 伪偏导重置 end u(k) u(k-1) rho * phi(k) / (lambda phi(k)^2) * (yd(k1) - y(k)); u(k) max(min(u(k), u_max), u_min); % 控制量限幅防止突变这里有几个容易踩的细节。第一dy必须对应y(k)和y(k-1)的差du必须对应u(k-1)和u(k-2)的差时序别搞反。第二重置条件除了判断φ(k)太小还要判断du绝对值太小否则分母接近0估计值会被噪声放大。第三φ0不能设成0否则控制器完全失去作用我习惯设成0.5到1之间。3.3 PFDL和FFDL的代码如何扩展PFDL相比CFDL的变化就是把标量φ变成了向量把Δu变成了过去L步输入增量组成的向量。核心估计式变成% PFDL伪梯度向量估计与控制律片段 dU [u(k-1) - u(k-2); u(k-2) - u(k-3); u(k-3) - u(k-4)]; % 假设L3 phi_p phi_p eta * dU / (mu dU*dU) * (dy - phi_p*dU); if norm(phi_p) eps1 || norm(dU) eps1 phi_p phi_p0; end u(k) u(k-1) rho * phi_p(1) / (lambda phi_p*phi_p) * (yd(k1) - y(k));注意PFDL的控制律分母里是伪梯度向量的范数平方分子只取第一个分量phi_p(1)。原理上控制器优化目标中用到的当前控制增量只与伪梯度向量的第一个分量直接相关后面的历史分量负责提高线性化精度而不是直接乘到控制增量上。这个细节如果不注意很容易把控制律写错导致仿真结果震荡。FFDL的代码本质上是PFDL的向量拼接版本% FFDL增量向量拼接Lu3, Ly2 dU [u(k-1)-u(k-2); u(k-2)-u(k-3); u(k-3)-u(k-4)]; dY [y(k)-y(k-1); y(k-1)-y(k-2)]; PhiVec [dU; dY]; Phi Phi eta * PhiVec / (mu PhiVec*PhiVec) * (dy - Phi*PhiVec); if norm(Phi) eps1 || norm(PhiVec) eps1 Phi Phi0; end u(k) u(k-1) rho * Phi(1) / (lambda Phi*Phi) * (yd(k1) - y(k));FFDL里要维护的初始向量Phi0长度就是LuLy。重置条件和PFDL相同用向量范数判断。整个核心逻辑不算复杂但每个分量初始值都会影响收敛速度建议初始化时统一设成0.5不要设得太离群。3.4 参数设置与整定心得MFAC参数不多但很敏感。以我复现时的一组初始参数为例参数CFDLPFDLFFDL作用ρ0.50.50.5控制律步长λ1.01.01.0控制律分母惩罚项η1.00.80.8伪偏导估计步长μ1.01.01.0伪偏导估计分母惩罚项伪偏导初始值1.0[1,0,0][1,0,0,0,0]估计器起点窗口Lu/Ly-Lu3Lu3, Ly2动态线性化窗口实际调试时不要死守这些数值。我的经验是先用λ1、ρ0.5起步观察输出曲线如果跟踪慢就减小λ如果出现明显振荡就增大λ并减小ρ。λ太小会让控制量放得太大系统容易抖λ太大会让控制量变化幅度不足跟踪变得迟钝。伪偏导估计侧的μ主要起防除零的作用不用太大μ1基本够用。4. 仿真实测与调试记录4.1 主程序流程与结果怎么看我建议把三个系统的主程序分开写每个系统一个入口脚本共享一套控制器函数。主流程大致是N 1000; t 0:N-1; yd [zeros(1,50), ones(1,200), -ones(1,200), ones(1,200), -ones(1,200), zeros(1,150)]; y zeros(1,N); u zeros(1,N); y(1) 0; y(2) 0; u(1) 0; u(2) 0; phi 1.0; for k 3:N-1 % 更新控制量 u(k) % 更新被控对象得到 y(k1) end figure; subplot(2,1,1); plot(t, yd, --, t, y, -); subplot(2,1,2); plot(t, u);方波参考轨迹下跟踪效果分三段看初始段是否有超调、跳变段能否快速跟上、稳态段有没有余差。伪偏导数φ(k)的曲线也建议单独画出来如果它在一个范围内平稳变化说明动态线性化估计器工作正常如果它频繁触发重置或者突然冲到几十、几百多半是参数或时序问题。4.2 调试中踩过的坑第一个坑伪偏导估计的单位不匹配。刚开始我把dy和du的时序弄反了导致φ(k)一会儿正一会儿负控制器输出振荡最后整个系统直接发散。后来在matlab里加了断点逐拍打印dy、du、phi的值才定位到是时序错位。建议你调试时一定把变量打印出来看别嫌麻烦。第二个坑控制量没有限幅方波参考突变瞬间u会冲得很大。仿真里虽然不会“烧坏”对象但会让伪偏导估计器剧烈更新后面动态恶化。加上u(k)限幅后系统稳定性明显改善。限幅范围我一般设为[-5, 5]具体看被控对象输入范围。第三个坑FFDL参数向量初始化太“干净”。我把Phi0全设成0结果前几步控制量几乎没反应系统迟迟不启动。后来把Phi0第一个分量设成0.5其余设成0.05控制器很快就跟上参考了。原因是伪梯度向量初始值决定了第一个控制增量的方向不能全是0。第四个坑PFDL窗口L设得太大。我试着把L从3改到8结果伪梯度向量长了估计器需要同时辨识8个分量收敛速度变慢跟踪曲线反而变差。这个现象说明动态线性化窗口并不是越长越好还是要根据对象实际输入滞后特性来定。4.3 常见问题速查表现象可能原因处理办法输出完全不跟踪参考轨迹伪偏导初始值太小或控制器分母λ过大增大初始φ0减小λ到0.1~1控制器输出高频抖振λ过小或ρ过大增大λ降低ρ到0.3~0.5伪偏导数频繁跳到初始值重置阈值eps1过高或du归一化不当减小eps1检查信号单位系统前期发散后期稳定控制量瞬时过大未限幅对u(k)加限幅减小方波幅度PFDL效果比CFDL还差窗口L过大或伪梯度初始向量不合理减小L到2~3初始化靠近真实增益FFDL参数过多无法收敛Lu、Ly都偏大先保持Lu3、Ly2不要贪多这张表基本覆盖了我复现过程中碰到的绝大多数问题。如果你跑出来的现象不在表里最有效的手段依然是“打点看数据”逐拍检查Δy、Δu、伪偏导估计值和控制量顺着数据流一定能找到异常环节。5. 几点个人体会5.1 复现这类算法的正确姿势MFAC最大的优点是不需要精确模型但这也意味着它不是一个“调一把参数就完事”的算法。我强烈建议按照CFDL → PFDL → FFDL的顺序来复现每一步都确认仿真曲线稳定再进入下一步。直接上FFDL参数多、窗口长一旦发散你连是哪个分量出了问题都很难定位。先把CFDL跑通等于把一个正确闭环的控制逻辑练熟了后面扩展只是增加向量维数而已。另外一定要把伪偏导数的变化曲线当成重要观测对象。很多文章只给输出跟踪曲线好像控制效果很好其实伪偏导估计可能早就在剧烈跳动了。控制仿真里过程可观不可控是最大的隐患。多画一张φ(k)子图既能帮你自己判断算法是否正常放论文里也显得分析更扎实。5.2 值得尝试的后续扩展方向如果这个基础复现做完之后还想继续深入有几个方向我认为性价比很高。一是把MFAC和PID结合在无模型框架下引入传统PID结构很多场景下鲁棒性会更好二是针对MIMO系统做扩展动态线性化从标量变成向量后伪偏导估计器和控制律的推导会更复杂但思路仍然一致三是把三种算法用Matlab的OOP架构封装成类切换CFDL、PFDL、FFDL只需要改一个参数后续做对比实验会方便很多这个我后面也打算专门封装一版。我个人复现下来最深的体会是MFAC不是万能药但它在“拿不到精确模型、只有输入输出数据”的场景里确实很有竞争力。如果你是刚开始接触这三种动态线性化建议先把CFDL调通再一点一点往PFDL和FFDL上加历史信息每个版本都保留一段仿真结果后面写报告和改论文都不至于手忙脚乱。
阅读完成 · 觉得有帮助?
咨询建站