资讯详情

异构多智能体系统输出一致性控制:Matlab/Simulink建模与仿真全复盘

📅 2026/10/1 4:09:17 | 华诺云谱 👁 阅读
异构多智能体系统输出一致性控制:Matlab/Simulink建模与仿真全复盘
异构多智能体系统分布式一致性这个课题我在实验里栽过最大的跟头就是一开始把“一致”理解成所有智能体状态都变成同一个向量。直到Simulink里跑出几条死活不重合的状态曲线我才回头去查文献发现自己面对的是“输出一致性”问题而不是经典教材里那种同构全状态一致性。后面我换成“输出一致”的角度Matlab加Simulink联合仿真才真正顺起来。这篇东西不是复述教材而是一次完整复盘异构多智能体系统在Matlab里怎么建模、控制器怎么设计、Simulink模型怎么组织以及最后如何判断一致性是否真的达成。适合正在做多智能体一致性仿真、却被异构建模搞得头大的人参考。1. 先想清楚异构系统的一致目标到底是状态还是输出1.1 状态一致性与输出一致性的一字之差多智能体一致性字面意思就是让一群个体的某个指标趋向相同。经典教材里有一大类结果是针对同构系统的状态一致性所有智能体的动态方程完全一样只是初始值不同于是可以要求完整状态向量满足[ \lim_{t\to\infty}|x_i(t)-x_j(t)|0,\quad \forall i,j ]这一套在理论推导里非常漂亮用拉普拉斯矩阵配合图论做稳定性和收敛速率分析都很有条理。但一旦引入“异构”麻烦就来了异构意味着不同智能体可能有不同的系统矩阵甚至状态维度都不一样。举个最直白的例子一阶积分器智能体状态是标量 (p_i)二阶积分器智能体状态是向量 ((p_i,v_i))。两个系统维数不同你在Simulink里把它们的完整状态信号拿来相减都是问题更不用说让状态向量相等。所以异构系统里通常讨论的是输出一致性[ \lim_{t\to\infty}|y_i(t)-y_j(t)|0,\quad \forall i,j ]输出可以是位置、温度、转速这类真正需要同步的物理量。至于内部其他状态是否一致不做要求。做课题时把这个想清楚后面的建模和仿真才不会跑偏。1.2 本文采用的系统设定由于原课题没有规定具体的动力学模型和规模我选了一套有代表性的设定5个智能体每个智能体动态类型不同有积分型、惯性型、振荡型和开环不稳定型通信拓扑为5节点有向图研究目标是输出一致性即5个智能体的输出 (y_i) 最终收敛到同一个常数值。这个设定兼顾了“异构”的典型性和仿真的可复现性。如果你的课题是3个智能体或者6个智能体结构上完全不需要变只需要修改邻接矩阵、系统参数和初始值即可。2. 仿真设定五种典型异构动态加有向通信拓扑2.1 五种被控对象的选择逻辑我特意让5个智能体覆盖常见的动态类型而不是随便凑数智能体动态方程状态维数特征1(\dot{x}u)1一阶积分器最基础的积分型对象2(\dot{x}_1x_2,\quad \dot{x}_2-2x_1-0.5x_2u)2带阻尼的振荡型对象有内部动态3(\dot{x}-x2u)1一阶惯性环节响应滞后4(\dot{x}_1x_2,\quad \dot{x}_2u)2双积分器典型的位置-速度模型5(\dot{x}0.5xu)1开环不稳定对象必须靠反馈镇定选择这组的理由是积分器、双积分器覆盖了最常见的工程对象振荡型带有内部动态不符合“一阶一致性问题”的经典假设一阶惯性环节说明时间常数不同也会造成名义上的异构开环不稳定对象则进一步检验控制器的镇定能力。五个智能体覆盖了从纯积分到不稳定系统的典型差异做出来的结果更有说服力。2.2 通信拓扑与拉普拉斯矩阵所谓“网络化”和“分布式”指的是每个智能体只能通过通信网络获取邻居的信息不存在一个集中式控制器看到所有智能体的全局状态。通信关系就抽象成一张有向图 (G)节点是智能体箭头表示信息流向。我选的拓扑是一个5节点强连通有向图1→2→3→4→5→1形成一条环另外智能体2还能收到智能体5的信息5→2。邻接矩阵用 (W) 表示其中 (W(i,j)1) 表示节点j的信息能传到节点in 5; W zeros(n); W(1,5) 1; % 节点5 传给节点1 W(2,1) 1; % 节点1 传给节点2 W(2,5) 1; % 节点5 传给节点2 W(3,2) 1; % 节点2 传给节点3 W(4,3) 1; % 节点3 传给节点4 W(5,4) 1; % 节点4 传给节点5拉普拉斯矩阵 (L D - W)其中 (D) 是对角阵对角线元素 (D(i,i)) 是节点i的入度D diag(sum(W, 2)); L D - W;这个 (L) 算出来是[ L \begin{bmatrix} 1 0 0 0 -1 \ -1 2 0 0 -1 \ 0 -1 1 0 0 \ 0 0 -1 1 0 \ 0 0 0 -1 1 \end{bmatrix} ]注意这是一个非对称矩阵因为图是有向的。判断图是否强连通不需要人工看箭头直接检查 (L) 的零特征值重数强连通图对应的拉普拉斯矩阵零特征值代数重数为1也就是说第二个最小特征值谱间隙大于0。用Matlab验证时要注意非对称矩阵的特征值可能是复数不能直接sort后取第2个ev eig(L); [~, idx] sort(real(ev)); lam2 ev(idx(2)); fprintf(谱间隙实部 %.4f\n, real(lam2));一个小提示sort(eig(L))对复数矩阵的排序规则和直觉不一样我吃过亏最好先取实部排序再看虚部是否可忽略。3. 控制器设计内模估计加状态反馈的分布式控制律3.1 为什么需要一个内模估计器我们的目标是让所有智能体输出收敛到同一个常数。这个“常数”本身是未知的而且必须通过分布式方式求出来不能由某个中心节点广播。工程上非常自然的做法是每个智能体内部维护一个参考估计 (z_i)只根据邻居输出差值去更新它[ \dot{z}i \mu \sum{j\in N_i} W(i,j),(y_i - y_j) ]其中 (\mu0) 是估计器增益。这个方程在矩阵形式下就是[ \dot{z} \mu L y ]它本质上是分布式求解一致值当图强连通时所有 (z_i) 会收敛到同一个常数但注意这个常数未必等于初始值的平均值因为 (L) 不对称。这和“平均一致性”是两回事只有入度和出度都相等的平衡图才会收敛到平均值。我一开始常在这里纠结为什么最终公共值不是初值平均值后来想明白了平均一致性要求无向图或者有向平衡图而我用的是强连通非平衡图最终值由拉普拉斯矩阵的左零空间决定。仿真时看到最终值不是平均别慌。3.2 输出调节方程求解 X_i 和 U_i如果参考信号是常数那么可以用一个非常经典的工具——输出调节理论。假设期望输出 (y_i\to z_i)并且 (z_i) 最终稳定在某个常数v。控制器可以通过前馈项让每个智能体在稳态时恰好输出v。需要求解一组代数方程[ A_i X_i B_i U_i X_i S,\qquad C_i X_i Q ]这里 (S0) 对应常值参考(Q1) 表示输出调节到常数本身。直观理解(X_i) 告诉状态应该待在哪个工作点(U_i) 告诉输入应该保持多大。比如双积分器稳态时位置要为v速度必须为0输入也必须为0于是 (X_i[1;0])(U_i0)。再比如开环不稳定对象 (\dot{x}0.5xu)稳态时 (0.5vU_i\cdot v0)所以 (U_i-0.5)。5个智能体的 (X_i, U_i) 如下智能体状态方程(X_i)(U_i)1(\dot{x}u)102(\begin{bmatrix}0 1\ -2 -0.5\end{bmatrix}x\begin{bmatrix}0\1\end{bmatrix}u)([1;0])23(\dot{x}-x2u)10.54(\begin{bmatrix}0 1\ 0 0\end{bmatrix}x\begin{bmatrix}0\1\end{bmatrix}u)([1;0])05(\dot{x}0.5xu)1-0.5这个表是整个控制器的“静态配置”每个智能体只需要自己那一行不需要知道邻居的模型。这就是异构的好处模型不同没关系只要能解出自己的调节器参数即可。3.3 状态反馈镇定与控制器结构有了工作点 ((X_i z_i, U_i z_i))还需要把状态拉到工作点附近。完整的控制律设计成[ u_i U_i z_i K_i (x_i - X_i z_i) ]第一项负责前馈第二项负责反馈镇定。其中 (K_i) 是单个智能体的状态反馈增益要求让 (A_iB_iK_i) 是Hurwitz矩阵也就是闭环特征值在左半平面。我用的增益如下K{1} -2; % 闭环极点 -2 K{2} [-2 -1]; % 闭环极点 -0.75 ± 1.85i K{3} -1; % 闭环极点 -3 K{4} [-2 -2]; % 闭环极点 -1 ± i K{5} -2; % 闭环极点 -1.5这个控制器结构在文献里很常见称为“内模 状态反馈”结构。常数参考信号对应的内模就是一个积分器所以每个智能体都有一个自己的 (z_i) 积分器。当 (\mu) 取得足够大时所有 (z_i) 先同步输出再跟随 (z_i) 同步整体形成两层的收敛过程。4. Matlab实现用主循环脚本把一致性过程跑起来4.1 为什么要先写脚本不直接上Simulink我的习惯是先在Matlab脚本里把数学模型验证清楚再搭Simulink。原因很简单脚本可以逐行debug可以随时打印中间变量数据结构也好控制。异构系统里每个智能体状态维度不同在脚本里用cell或者分块索引很直观但在Simulink里不同维度的信号混在一起反而容易乱。4.2 状态组织与初始值把7个状态按顺序拼成一个列向量其中智能体2和智能体4各占2个状态x(1)智能体1的状态x(2:3)智能体2的位置和速度x(4)智能体3的状态x(5:6)智能体4的位置和速度x(7)智能体5的状态z(1:5)5个智能体的参考估计初始值我习惯让5个智能体输出差得远一点更容易看出收敛过程x0 [1; 0; 0; -1.5; 2; 0; -2]; z0 zeros(5, 1);4.3 核心动态函数用ode45跑数值积分把整段动态写成一个函数function dX fullDynamics(~, X, L, K, Xi, Ui, mu) x1 X(1); x2 X(2:3); x3 X(4); x4 X(5:6); x5 X(7); z X(8:12); % 输出向量 y [x1; x2(1); x3; x4(1); x5]; % 参考估计器 dz mu * (L * y); % 控制量 u zeros(5, 1); u(1) Ui{1}*z(1) K{1}*(x1 - Xi{1}*z(1)); u(2) Ui{2}*z(2) K{2}*(x2 - Xi{2}*z(2)); u(3) Ui{3}*z(3) K{3}*(x3 - Xi{3}*z(3)); u(4) Ui{4}*z(4) K{4}*(x4 - Xi{4}*z(4)); u(5) Ui{5}*z(5) K{5}*(x5 - Xi{5}*z(5)); % 各智能体状态导数 dx1 u(1); dx2 [x2(2); -2*x2(1) - 0.5*x2(2) u(2)]; dx3 -x3 2*u(3); dx4 [x4(2); u(4)]; dx5 0.5*x5 u(5); dX [dx1; dx2; dx3; dx4; dx5; dz]; end主脚本只需要组装参数、调用ode45、画图mu 10; opt odeset(RelTol, 1e-6, AbsTol, 1e-8); [t, X] ode45((t,X) fullDynamics(t, X, L, K, Xi, Ui, mu), [0 12], [x0; z0], opt); y X(:, [1, 2, 4, 5, 7]); % 取出5个输出 plot(t, y, LineWidth, 1.5);需要提醒的是K、Xi、Ui最好用cell数组存放每个智能体一个元素。这样后面扩展到更多异构智能体时只需要往cell里加内容不用大改函数。4.4 一致性误差怎么记录单独看输出曲线还不够最好同时记录一致性误差eMax max(abs(y - mean(y, 2)), [], 2); semilogy(t, eMax);这个误差是每个时刻5个输出与当前平均值的最大偏差收敛到接近0就说明一致性达成。把它画成对数坐标后收敛速度一目了然。如果发现误差不再下降而是停留在某个水平多半是离散化误差或者控制器参数有问题。5. Simulink建模按智能体划分子系统而不是堆一个大模型5.1 顶层结构把通信拓扑变成信号连线Simulink仿真的核心思路是“一个智能体一个子系统”和论文里的结构保持一致。顶层建立5个Subsystem分别命名Agent1到Agent5。每个Agent有一个输出端口y_i根据拓扑把对应信号连到需要它的智能体。比如智能体2需要智能体1和智能体5的输出用Mux把y1和y5拼成一个2维向量送入Agent2的输入端。这种连线方式最直观适合节点数少于10个的情况。如果以后节点很多我建议改用Goto/From或者Bus对象否则顶层的线会像蜘蛛网一样乱。5.2 Agent内部搭建积分器加MATLAB Function以智能体4为例状态是位置x4(1)和速度x4(2)。内部结构是两个积分器分别积累位置和速度一个积分器作为参考估计 z4一个MATLAB Function块实现控制律 (u_4) 和 (dz_4)。MATLAB Function块的代码可以这样写function [u4, dz4] agent4Ctrl(pos, vel, z, yN, mu) dySum sum(yN) - 2*pos; % 智能体4入度为2? 实际上按拓扑不同需修改 dz4 mu * dySum; u4 0*z [-2 -2] * ([pos; vel] - [1; 0]*z); end这段代码要按拓扑关系微调。比如智能体4只接收智能体3的信息入度是1那dySum yN(1) - pos。更通用的写法是给每个子系统的内部模块接受一个neighbor下标向量但那样会提高信号组织的复杂度。对于教学和毕业设计来说每个Agent内部单独写一个函数反而更清晰。我踩过的一个小坑在MATLAB Function块里写y_i - y_j时一定要分清哪个是自己的输出哪个是邻居输出。表达式写反了符号就乱了而且很难从曲线里一眼看出问题。5.3 求解器设置与采样问题Simulink里我建议使用固定步长求解器选ode4四阶龙格库塔步长0.001。固定步长的好处是结果可复现而且在后面做代码生成、半实物仿真时不会遇到变步长带来的随机性。如果是纯学术展示变步长ode45也可以但固定步长更接近实际控制系统实现。如果你希望模拟“网络通信是有采样周期”的可以在每个Agent输入侧加一个Zero-Order Hold把连续输出变成周期采样的离散信号采样周期比如0.01s。这样做之后一致性误差不会严格收敛到0只会收敛到和采样周期相关的一个有界区域。这是一个值得做的进阶对比实验能直观看到通信采样对收敛精度的影响。6. 结果分析一致性误差、参考估计与收敛速度的判据6.1 输出曲线与参考估计曲线跑完12秒仿真正常结果应该是5条输出曲线从不同初始值出发在2到4秒内汇合到同一个常数。同时记录z1到z5也应该收敛到同一个值而且这个值就和输出最终值相同。z和y的最终一致性可以从两个层面交叉验证z是“内部虚拟目标”y是“实际输出”两者同时收敛说明控制器的内模估计起了作用而不是仅仅把误差压小了但参考估计各干各的。6.2 一致性误差的收敛判定我通常用最大误差[ e(t)\max_{i,j}|y_i(t)-y_j(t)| ]判断标准看用途如果只是想演示一致性(e10^{-3}) 就足够如果要做毕业设计严格验证我会继续看它是否收敛到 (10^{-6}) 量级。在matlab的ode45下连续系统一般能压到 (10^{-8}) 以下Simulink固定步长处会有数值误差通常在 (10^{-5}) 附近就很不错了。如果在误差曲线上看到一个“平台”不再下降先检查是不是步长太大再检查是不是控制器存在稳态偏差。6.3 参数对收敛速度的影响参数调参时我有几条经验(\mu) 越大z_i同步越快整体输出收敛越快但过大时z_i的极点会变快Simulink固定步长可能需要更小步长。我习惯 (\mu) 从5试到20。(K_i) 决定单个智能体跟踪工作点的带宽设置过小会让某个智能体拖后腿设置过大会出现超调。每个智能体的K可以先单独做单智能体跟踪仿真确认稳定再接入分布式网络。拓扑的谱间隙决定最终收敛速率。如果把那条额外的5→2边删掉收敛会明显变慢如果把某条边断开导致图不强连通输出就不再一致。做对比实验时这个现象特别有说服力。6.4 一个容易被误读的现象由于图是非平衡的最终一致值不等于初始平均值。比如初始值分别为1、0、-1.5、2、-2平均值为-0.1但最终一致值可能落在另一个位置。这取决于左特征向量和初值的关系。很多第一次做有向图仿真的同学会以为控制协议写错了其实协议没有错只是平均一致性需要额外的平衡图条件。这点在写结论时要写清楚否则答辩时容易被打。7. 我踩过的坑拓扑矩阵方向、代数环和步长选择7.1 拉普拉斯矩阵方向弄反导致的“伪发散”这是我犯得最隐蔽的错误。邻接矩阵如果把信息方向定义反了也就是把出度矩阵当成了入度矩阵算出来的 (L) 虽然行和仍然是0但控制项符号完全相反本来的负反馈变成正反馈仿真结果就是输出发散或者剧烈振荡。排查方法很简单先做一个静态测试所有智能体输出相同即 (y) 为常数向量那么 (L y) 必须等于0。更直接一点用代码断言assert(isequal(round(L * ones(n, 1)), zeros(n, 1)));如果这行失败说明拉普拉斯矩阵定义有问题先别调控制参数。7.2 把输出一致性误判成状态不一致异构系统仿真结果里最容易出现的一个“假失败”看输出曲线已经重合了但把完整状态画出来发现某个智能体的速度是0.1另一个速度是-0.4于是误以为没有收敛。这不是bug而是异构系统的正常现象。二阶智能体内部有额外状态这些状态受各自的动态约束没有义务与别的智能体相同。保存数据时最好只保存输出量或者单独画输出图避免混淆。7.3 Simulink代数环遇到就要处理搭建Agent内部模型时如果控制输入直接从输出y反算并且信号回路里没有积分器或者 Unit DelaySimulink会在编译时提示检测到代数环。代数环会引入隐式求解影响仿真速度更严重的是在生成C代码时可能无法直接部署。解决办法通常有两个一是把y信号经过一个Unit Delay模拟通信延迟顺便破除代数环二是重新整理控制结构让控制量只依赖于积分器状态而不是直接依赖于当前输出。顺带提一句用Bus信号做顶层信号路由时如果碰到Bus Selector没有可选信号多数是总线信号没有正确命名或者连接处出现了隐式类型转换检查一下创建Bus的模块和Select块的信号名是否完全一致。这个问题和代数环一样都是Simulink建模时容易卡住人的地方。7.4 固定步长与离散化误差我在Simulink里常用固定步长ode4但固定步长下快动态对象很容易出现“锯齿状”收敛曲线。判断步长是否足够有个笨办法把步长从0.001改成0.005如果输出曲线的收敛趋势有明显变化说明0.005不够如果几乎一致说明当前步长安全。另外如果 (\mu) 调到30以上z_i的动态会快很多即使输出本身不快估计器也可能因为步长太大出现数值振荡。遇到这种情况先降 (\mu)再决定是否缩小步长。到现在为止这个仿真例子已经能从头跑通。回顾整个流程我最想分享的其实是一个习惯动手搭Simulink之前先用Matlab脚本把数学模型跑明白做好一致性误差的对数曲线。模型出问题时先检查拓扑、再检查内模参数比在Gain模块之间漫无目的地找错效率高得多。往后的扩展方向也很明确给控制协议加入通信时延、丢包或量化误差或者把常值参考改成斜坡、正弦参考那时只需把内模矩阵S从0换成对应的模态矩阵整个代码和模型框架都不用大改。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑