含通信与输入时滞的多智能体一致性仿真:dde23与Simulink实现
发布时间:2026/9/20 4:17:57来源:尧图网络
做多智能体协同控制的老哥十有八九都遇到过这种情况仿真模型里跑得漂漂亮亮的一致性曲线一接上真实通信链路状态量就开始抖严重的时候直接发散。问题往往出在两个地方——通信时滞和输入时滞。这篇文章用一个最简单的4智能体一阶积分器模型把这两类时滞同时加进去给出Matlab脚本和Simulink两种仿真实现最终画出一张干净直观的多智能体一致性仿真图。整个项目适合刚入坑协同控制、或者正在做毕设仿真验证的同学参考代码和参数可以直接抄。先交代一下本文会做到什么程度用理论把两种时滞的位置讲清楚再给出一个“总时滞相加”的结论然后用dde23数值求解和 Simulink 分步建模两条路线做仿真验证。最后附上几个我在实际调试中踩过的坑帮你少走弯路。1. 一致性仿真的两个关键点协议与时滞1.1 一阶一致性协议到底在做什么多智能体一致性的目标很直观让所有智能体的状态 (x_i(t)) 随时间的推移逐渐趋于同一个值。工程上最常用的是分布式一阶协议[ u_i(t) -\sum_{j \in N_i} a_{ij}\left(x_i(t) - x_j(t)\right) ]这里的 (a_{ij}) 是通信拓扑的邻接矩阵元素只有智能体 (i) 能收到智能体 (j) 的信息时(a_{ij}) 才不为零。把所有智能体写成一个状态向量 (x [x_1, x_2, \dots, x_n]^T)协议变成矩阵形式[ u(t) -L x(t) ]其中 (L) 是图拉普拉斯矩阵。(L) 有一个特别好的性质每一行元素之和为零。这意味着对于全1向量 (\mathbf{1}) 有 (L\mathbf{1}0)所以当所有智能体状态相等时控制输入恰好为零系统进入平衡状态。这正是“一致性”成立的理论基础。在无时滞的理想情况下闭环系统是[ \dot{x}(t) -L x(t) ]只要通信拓扑是连通的状态就会指数收敛到初始状态的某个加权平均值。可一旦把时滞加进去情况就变了系统可能从收敛变成振荡甚至发散。1.2 通信时滞和输入时滞在系统中位置不同通信时滞和输入时滞虽然都叫“时滞”但它们在系统里出现的位置完全不同物理来源也不一样。通信时滞发生在“信息从传感器/远端传到控制器”这一步。比如智能体 (j) 在 (t) 时刻发出的状态 (x_j(t))智能体 (i) 要等到 (t \tau_c) 才能收到。于是协议变成[ u_i(t) -\sum_{j \in N_i} a_{ij}\left(x_i(t - \tau_c) - x_j(t - \tau_c)\right) ]注意这里连 (x_i(t-\tau_c)) 也必须用本地记忆的历史值因为控制律里比较的是“同一时刻收到的状态”否则 (x_i) 和 (x_j) 的时间基准会对不上。矩阵形式就是[ u(t) -L x(t - \tau_c) ]输入时滞则发生在“控制器输出到执行器真正作用”这一步。控制器算出了 (u_i(t))但执行机构、D/A转换、机械惯性都需要时间实际作用到系统上的可能是 (u_i(t - \tau_u))。于是对象模型变成[ \dot{x}_i(t) u_i(t - \tau_u) ]两类时滞对系统稳定性的影响从数学上都能写成延迟微分方程但物理上一个是“输入信息旧了”一个是“执行动作慢了”。在仿真建模时如果混为一谈后面调试会非常痛苦。时滞类型产生环节数学位置典型来源通信时滞 (\tau_c)状态采集与传输控制协议的变量 (x(t-\tau_c))网络传输、传感器采样、数据排队输入时滞 (\tau_u)控制器到执行器对象模型 (\dot{x}(t)u(t-\tau_u))执行机构响应、D/A转换、机械惯性1.3 为什么仿真时经常把两者合并成“总时滞”把两类时滞同时放进一阶积分器系统[ \dot{x}_i(t) u_i(t - \tau_u) ][ u_i(t) -\sum_{j \in N_i} a_{ij}\left(x_i(t - \tau_c) - x_j(t - \tau_c)\right) ]做一次变量替换把 (t) 换成 (t - \tau_u)代入之后你会发现[ \dot{x}i(t) -\sum{j \in N_i} a_{ij}\left(x_i(t - \tau_c - \tau_u) - x_j(t - \tau_c - \tau_u)\right) ]也就是说在线性时不变、且所有链路延迟相同的前提下通信时滞和输入时滞是“累加”进闭环系统的合并成一个等效总时滞[ \tau \tau_c \tau_u ]这是本文仿真里最核心的一个结论。很多论文里写“同时存在通信时滞和输入时滞”时最后的稳定性条件往往只和一个总延迟有关原因就在这里。后续的仿真也是基于这个结论来设置参数的。当然在有向切换拓扑、时变时滞、非线性协议下这种合并就不再严格成立了需要分开处理。2. 仿真系统搭建与关键参数确定2.1 为什么选4个智能体无向环拓扑仿真图要直观智能体数量不能太多也不要太少。4个智能体的状态曲线放在一张图里刚刚好每一条线都清晰可见。更重要的是我选的拓扑是无向环智能体1连2和4智能体2连1和3智能体3连2和4智能体4连3和1。无向环的拉普拉斯矩阵特征值很有规律对于4节点环来说[ \lambda(L) {0, 2, 2, 4} ]存在一个较大的特征值4能为后面的理论临界时滞计算提供整数参数方便验证。另一个关键点是环拓扑是连通的如果不连通一致性根本不可能实现这一点在选型时要注意。邻接矩阵取[ A \begin{bmatrix} 0 1 0 1\ 1 0 1 0\ 0 1 0 1\ 1 0 1 0 \end{bmatrix} ]度矩阵 (D \mathrm{diag}(2,2,2,2))拉普拉斯矩阵[ L D - A \begin{bmatrix} 2 -1 0 -1\ -1 2 -1 0\ 0 -1 2 -1\ -1 0 -1 2 \end{bmatrix} ]初始状态我取分散一点的值[ x(0) [1,; -0.5,; 2,; -1]^T ]这样仿真图里能明显看到四条曲线从不同位置出发最后汇聚到同一个值视觉上非常直观。2.2 时滞临界值估算一个很有用的理论公式对一阶积分器多智能体系统考虑时滞后的闭环方程[ \dot{x}(t) -L x(t - \tau) ]因为拉普拉斯矩阵是对称阵可以对角化分解。除去零特征值对应的“一致性流形”外每个非零特征值 (\lambda) 都对应一个标量子系统[ \dot{v}(t) -\lambda v(t - \tau) ]设解为 (v(t) v_0 e^{s t})代入后得到特征方程[ s \lambda e^{-s\tau} 0 ]系统从稳定变成不稳定的临界点必然出现在纯虚根 (s j\omega) 处。把 (s j\omega) 代进去令实部虚部分别为零可以得到[ \omega \lambda, \qquad \tau_{\max} \frac{\pi}{2\lambda} ]也就是说对某个特征值 (\lambda)时滞一旦超过 (\pi / (2\lambda))对应模式就会失稳。整个系统能承受的最大时滞由最大的非零特征值决定[ \tau_{\max} \frac{\pi}{2\lambda_{\max}} ]对4节点无向环(\lambda_{\max} 4)所以[ \tau_{\max} \frac{\pi}{8} \approx 0.3927\text{s} ]这个数非常关键后面所有仿真参数都围绕它设计。比如总时滞0.2s远小于临界值系统快速收敛总时滞0.35s接近临界值曲线出现明显振荡总时滞超过0.4s系统就会发散。这样仿真图才有“由稳到不稳”的层次感比只跑一组参数有说服力得多。2.3 参数表与实验组设计仿真总时长取 (T 20\text{s})足够让系统在时滞较小时进入稳态。数值容差用ddeset设为 (10^{-6}) 和 (10^{-8})保证精度。实验组(\tau_c) (s)(\tau_u) (s)总时滞 (\tau) (s)预期现象10.050.050.10快速收敛曲线平滑20.150.200.35收敛但明显振荡30.200.200.40临界状态等幅振荡40.300.300.60发散系统失稳还有一组对比实验用来验证“总时滞相加”的结论固定总时滞0.3s不变分别取 ((\tau_c,\tau_u)) 为 (0.05,0.25)、(0.15,0.15)、(0.25,0.05)三条仿真曲线应该几乎重合。这组对比很有意思能直观证明两类时滞在数学上的等效性。3. 基于Matlab dde23的仿真实现3.1 为什么用dde23而不是ode45带时滞的微分方程不是普通ODE当前时刻的导数依赖过去某个时刻的状态。如果你用 ode45 硬算需要自己维护一个“历史状态缓冲区”每次计算导数时都要查表插值非常容易出bug。Matlab自带的dde23就是专门求解延迟微分方程的内置了历史函数管理和变步长控制。dde23的核心输入有三部分dydt导数函数格式是(t,y,Z) ...其中Z(:,k)对应第 (k) 个延迟项 (y(t - \tau_k))lags延迟值向量可以是多个延迟history历史函数表示 (t \le t_0) 时的状态取值对我们的系统闭环方程是[ \dot{x}(t) -L x\left(t - (\tau_c \tau_u)\right) ]只有一个延迟所以lags tau_c tau_u导数函数里用Z(:,1)即可。3.2 完整可运行的Matlab代码% 多智能体一致性仿真含通信时滞和输入时滞 % 模型: dx/dt -L * x(t - (tau_c tau_u)) clear; close all; clc; %% 1. 参数设置 n 4; % 智能体数量 A [0 1 0 1; 1 0 1 0; 0 1 0 1; 1 0 1 0]; % 无向环邻接矩阵 D diag(sum(A, 2)); L D - A; % 拉普拉斯矩阵 tau_c 0.1; % 通信时滞 (s) tau_u 0.1; % 输入时滞 (s) tau tau_c tau_u; % 等效总时滞 (s) %% 2. 初始状态 x0 [1; -0.5; 2; -1]; %% 3. 使用 dde23 求解延迟微分方程 lags tau; history (t) x0; % t 0 时状态保持初值 dydt (t, y, Z) -L * Z(:, 1); % Z(:,1) y(t - tau) options ddeset(RelTol, 1e-6, AbsTol, 1e-8); sol dde23(dydt, lags, history, [0, 20], options); %% 4. 生成密集时间点并求值 t linspace(0, 20, 1000); y deval(sol, t); %% 5. 画多智能体一致性仿真图 figure(Color, w, Position, [100 100 720 460]); plot(t, y, LineWidth, 1.8); xlabel(时间 t (s), FontSize, 12); ylabel(状态 x_i(t), FontSize, 12); title([多智能体一致性仿真: \tau_c, num2str(tau_c), ... s, \tau_u, num2str(tau_u), s, 总时滞, num2str(tau), s], ... FontSize, 11); legend({Agent 1, Agent 2, Agent 3, Agent 4}, ... Location, best, FontSize, 10); grid on; xlim([0 20]);把tau_c和tau_u改成不同的值重新运行就能得到不同的仿真图。代码里有一个地方要特别说一下history (t) x0表示在仿真起点之前所有智能体状态都保持在初始值。这个设定在物理上等价于“系统在 (t0) 时刻才开始投入运行”这也是延迟微分方程求解的常规做法。3.3 仿真图怎么解读我这里描述一下第1组参数 ((\tau_c0.05, \tau_u0.05)) 的仿真图你可以照着对比自己的图四条曲线分别从1, -0.5, 2, -1出发前1秒内有明显的调整过程但方向一致地往中间值靠拢大约4秒后所有曲线重合最终收敛到初始值的平均值附近曲线没有过冲整体非常平滑把总时滞改成0.35s再跑你会看到曲线在收敛过程中来回穿越出现明显的“波浪形”过冲但最终仍然停在同一个值上。而总时滞0.4s时曲线会保持近似等幅振荡一直持续到仿真结束系统处于临界稳定状态。总时滞0.6s时曲线振幅会指数增长直接发散。这里再补一个验证“总时滞等效”的简单循环代码% 固定总时滞变换两类时滞的配比 tau_total 0.3; combos [0.05 0.25; 0.15 0.15; 0.25 0.05]; figure(Color, w, Position, [100 100 720 460]); hold on; for k 1:size(combos, 1) tau_c combos(k, 1); tau_u combos(k, 2); lags tau_total; history (t) x0; dydt (t, y, Z) -L * Z(:, 1); sol dde23(dydt, lags, history, [0, 20], options); t linspace(0, 20, 1000); y deval(sol, t); plot(t, y(1, :), LineWidth, 1.5, ... DisplayName, [\tau_c, num2str(tau_c), , \tau_u, num2str(tau_u)]); end hold off; xlabel(时间 t (s)); ylabel(状态 x_1(t)); title(固定总时滞0.3s通信时滞/输入时滞配比对系统的影响); legend(show); grid on;这个程序只画智能体1的状态曲线三条线会几乎重合证明只要总时滞相同系统动态就基本一致。实验截图的时候放这张图要比放四张完整图更有冲击力。3.4 分别观察两类时滞的方法如果你想单独观察通信时滞对系统的影响只需要把 (\tau_u) 设成0然后变大 (\tau_c)。反过来把 (\tau_c) 设成0只调 (\tau_u)。由于总时滞相加这两组实验的曲线形态会高度相似。但不要因此说“两类时滞完全等价”——它们在系统里的物理位置不同后续如果加入执行器饱和、量测噪声或者换成二阶系统两类时滞的影响就会显示出差异。4. 基于Simulink的分步建模与可视化4.1 Simulink模型结构思路Matlab脚本适合快速批量跑参数但Simulink模型的优势在于把两类时滞的物理位置摆得明明白白。我搭的模型结构是这样状态向量 (x) 作为模型内部状态先用一组Transport Delay模拟通信时滞把 (x) 延迟 (\tau_c)延迟后的信号经过Matrix Gain增益设为 (-L)得到的控制量再经过一组Transport Delay模拟输入时滞 (\tau_u)最终信号进入Integrator积分得到新的状态 (x)这个结构与理论推导完全对应通信时滞在状态进入控制律之前输入时滞在控制量进入对象之前。模型框图一眼就能看出两类时滞的不同位置。4.2 关键模块配置以Simulink R2020a及以上版本为例模块配置如下IntegratorInitial condition 填[1; -0.5; 2; -1]对应四个智能体的初始状态Transport Delay通信时滞Delay time 填0.1Initial input 填[1; -0.5; 2; -1]Matrix GainGain 填-L也就是最好在Matlab工作区算好L后直接填变量名Transport Delay输入时滞Delay time 填0.1Initial input 填[0; 0; 0; 0]这里有一个大坑通信侧Transport Delay的 Initial input 必须设置成初始状态向量否则仿真前0.1秒内延迟模块的输出是0控制律会突然出现一个巨大的跳变Simulink求解器可能直接报错或跑出离谱的曲线。输入侧Transport Delay的 Initial input 设为0比较合理因为控制器在 (t0) 时刻之前的输出默认是0表示系统启动前没有施加控制。4.3 求解器设置与Scope输出仿真时长设20s求解器选变步长ode45最大步长限制在0.01s以内。之所以要限制最大步长是因为Transport Delay内部有插值步长太大时延迟边沿附近的数值精度会变差曲线可能出现锯齿。Scope里用Demux把四路信号拆开分别显示x_1到x_4再加一个To Workspace模块保存数据到Matlab工作区方便后续用plot重新美化。Simulink跑出来的曲线理论上和dde23的结果完全一致。实际对比时只要仿真时长和参数一致两条曲线几乎重合差异只在数值求解精度范围内。这也说明模型搭建是对的。4.4 Simulink仿真截图要点发布博文或写报告时Simulink图建议包含两部分一张模型截图一张Scope曲线截图。模型截图要能清晰看到两层Transport Delay分别标注Communication Delay和Input DelayScope截图用白色背景线宽调粗一点加上图例和时间轴标签。如果你想把两条仿真曲线叠在一张图里可以在Matlab中执行plot(t, y); % y 来自 deval(sol, t) hold on; plot(simout.time, simout.signals.values, --); % simout 来自 To Workspace这样能直观看到两种仿真方法的结果一致作为方法验证非常有力。5. 常见问题与排查技巧实录5.1 仿真发散曲线直接飞出去这是我遇到最多的问题。原因是总时滞 (\tau_c \tau_u) 超过了临界值0.3927s。比如有人把两个时滞都设成0.3s总时滞0.6s系统必然发散。排查方法很简单先算出模型的理论临界时滞然后检查设置的延迟参数。如果确实想用大时滞做测试那必须同时降低控制器增益相当于人为扩大稳定裕度但收敛速度会变慢。5.2 曲线前段出现莫名其妙的水平线或跳变这个锅基本由Transport Delay的 Initial input 背。通信时滞模块的初始输入必须等于智能体初始状态。如果设为0前 (\tau_c) 秒内控制器收到的状态全是0控制量巨大系统起步就会乱跳。解决办法就是设置正确的 Initial input。5.3 dde23报错历史函数返回维度错误history函数返回的必须是一个 (n \times 1) 的列向量。如果你写成(t) [1, -0.5, 2, -1]返回的是行向量dde23会直接报错。正确写法是(t) [1; -0.5; 2; -1]或(t) x0。这个错误很隐蔽因为Matlab不会在定义阶段报错只在求解时提示维度不匹配。5.4 曲线最终没有完全收敛到同一个值原因有几个一是仿真时间不够长系统还没进入稳态二是时滞接近临界值收敛特别慢三是数值容差太大残差被放大。建议把RelTol设到 (10^{-6}) 以下仿真时间拉长到30s再看。5.5 常见问题速查表现象可能原因解决办法曲线发散总时滞超过临界值减小 (\tau_c) 或 (\tau_u)或降低控制增益起步阶段跳变Transport Delay 初始输入错误Initial input 设为初始状态收敛过慢时滞接近临界值或增益太小增大控制增益或缩短仿真步长曲线有锯齿固定步长太大或容差太松改用可变步长限制最大步长0.01sdde23报维度错误历史函数返回行向量改为列向量形式Simulink没有输出变量名冲突或L未在工作区先运行参数脚本再打开模型5.6 一个独家调试技巧先把理论临界时滞算出来比如本文的0.3927s。然后从低于临界值的0.1s起步每次增加0.05s跑一次记录收敛时间和振荡幅度。这样你能在一个小时之内画出系统的“时滞-动态”变化趋势比直接丢一个大时滞然后对着发散曲线干瞪眼高效太多了。我就是用这个方法两轮调试就确定了“0.35s开始明显振荡0.4s临界0.6s发散”的规律。6. 往复杂方向扩展的几个方向这篇文章里的模型是一阶积分器是最简单的多智能体一致性模型。实际项目里大家通常要面对的东西更多。第一个扩展方向是二阶模型。无人车、无人机编队通常建模成位置和速度两个状态此时一致性协议里要同时包含位置耦合和速度阻尼项。加入时滞之后位置回路和速度回路会相互耦合系统更容易出现高频振荡。我试过在二阶模型里把输入时滞设到0.2s不加额外阻尼的情况下速度状态会先发散。解决思路是加入速度反馈增益项等效于人为增加阻尼。第二个扩展方向是异构时滞。真实网络的每条通信链路的延迟并不相同有的链路0.1s有的链路0.3s。这时候不能简单合并成一个总时滞dde23里需要设置多个lagsZ矩阵的每一列对应不同的延迟链路。代码难度会上升一个台阶但物理意义更贴近实际。第三个方向是时变时滞和丢包。网络负载变化会导致延迟随时间变化甚至出现丢包。这类问题通常需要切换到网络化控制系统的方法把时滞建模为一个区间用LMI方法求解稳定性条件仿真上也要用离散事件模型配合连续动态模型。这已经超出本文的范畴了但底层的一致性协议逻辑仍然不变。我个人调试这类系统时最大的体会是先小后大先简单后复杂。用小拓扑、简单协议把稳定性边界摸清楚再逐步加时滞、加拓扑、加模型复杂度。每一步改动都保留上一轮的仿真图方便回退对比。时滞仿真不是越复杂越好图干净、结论清楚比堆砌一堆高级算法更能说明问题。希望这篇多智能体一致性时滞仿真的实现过程能帮你顺利跑出第一张属于自己的仿真图。
网站建设高端定制企业官网