MATLAB传递函数三图绘制与Simulink仿真对照避坑指南 简介面向控制系统、自动化与信号处理方向的学习者和工程师这份MATLAB/Simulink基础示例聚焦S平面上传递函数的可视化分析帮助读者把传递函数、复频率域响应与三维曲面图联系起来理解。压缩包共2个文件包含1个m脚本和1个txt说明文件整体约2KB体量轻巧m脚本承担传递函数建模、频率范围设置、频率响应计算及三维图形绘制等核心内容txt文件用于补充使用许可信息。已有1822人学习说明该主题在课程实验与工程入门中有一定参考需求。读者可借助其中的脚本梳理tf函数用法、S平面零极点与频率响应的关系并复现传递函数三维曲面图的生成过程为控制系统动态特性分析和后续Simulink建模打基础。1. 三张传递函数图为什么值得单独拆一份脚本Simulink 的模块拖得再顺碰到“把这个传递函数的阶跃响应、Bode 图、Nyquist 图都画出来并且和 Simulink 里跑的波形对上”还是会卡。卡的不是命令拼写而是不知道时间向量给多密、频率范围取到哪、三张图之间怎么互相印证以及仿真曲线和理论曲线对不上时先怀疑哪一步。这份 MATLAB 资源就冲这件事来三个典型传递函数一套绘图脚本一个 Simulink 对照模型把建模、画图、回收仿真数据、定量验证串成一条能照着敲的线。它适合刚补完 Simulink 基础、正准备做控制类课程设计的人也适合手上有现成模型但说不清稳定裕度从哪儿来的人。2. 建模入口tf、zpk、ss 三种写法谁更适合画图传递函数图能不能画准八成的锅其实在建模这一步就埋下了。系数顺序写反、零极点该消的没消、时延挂错位置后面调半天画图参数都是白忙。先把三个被控对象用三种不同写法立起来后面所有图都基于它们这样出了问题能快速定位是模型本身歪了还是绘图指令用错了。2.1 系数向量写法从分母降幂开始最常见的入口是tf(num, den)两个行向量分别给分子和分母的系数顺序必须是降幂缺项补零。新手翻车最多的地方是分母[1 3 2]被写成[1 2 3]画出来的曲线照样光滑只是对应的极点位置完全变了光看图很难察觉。% 三个典型对象后面的图都用它们 G1 tf(1, [1 0.6 1]); % 二阶欠阻尼zeta0.3, wn1 G2 tf([2 1], [1 3 2]); % 一阶零点配二阶极点 G3 tf(5, [1 1 0]); % 含积分环节阶跃下会爬升 % 检查极点位置确认系数没写错 disp(pole(G1)) % 应为 -0.3 ± 0.9539i disp(zero(G2)) % 应为 -0.5 disp(pole(G2)) % 应为 -1 和 -2pole和zero这两个函数是建模阶段最便宜的后悔药写完立刻打印一下比画完图再回头怀疑省事得多。G1的阻尼比 0.3、自然频率 1是我习惯用来当“有明显超调但不至于振荡到看不清”的参照对象G2带一个左半平面零点用来观察零点对相位和超调的拉扯G3分母常数项为 0属于典型非真分式之外的另一种特殊情况——积分环节会让低频增益趋于无穷Bode 图低频段是一条 -20dB/dec 的斜线正好用来验证频率范围有没有取对。提示tf不会自动约分。如果分子分母有公因式先跑一遍minreal否则零极点对消会在 Nyquist 图上伪装成一次包围给出完全错误的稳定性结论。2.2 零极点增益与状态空间什么时候必须换写法zpk的写法是zpk(z, p, k)直接给零点、极点、增益从频域角度看反而更直观——Bode 图的每一个转折点对应一个极点或零点斜率变化一目了然。做频域设计时我一般先用zpk把对象描述清楚再转成tf交给绘图函数因为bode、nyquist对两种写法都认但zpk形式在手工核对转折频率时不容易看错。% 零极点增益写法零点 -0.5极点 -1/-2增益 2 Gz zpk(-0.5, [-1 -2], 2); % 转成传递函数形式核对系数是否与 G2 一致 Gt tf(Gz); disp(Gt) % 状态空间形式适合后续做能控能观分析 Gss ss(G1); disp(size(Gss.A)) % 2x2二阶系统两个状态ss这条路径在只画三张图时用得不多但有两种场景绕不开一是对象本身来自机理建模天生是状态方程二是分子阶数接近分母阶数时tf的数值精度会开始抖动转成状态空间再算反而更稳。ss(G1)之后可以用tf(Gss)转回来对照两个方向都跑一遍系数对得上说明这条链路没有精度损失。需要留意的是状态空间转传递函数在极点密集时会掉精度尤其是高阶级联系统。三张图的场景里二阶、三阶还不至于出问题但如果后面要把多个模块串起来做闭环minreal该加还是得加。2.3 时延与离散采样时间怎么挂上去连续系统画图不需要额外参数但一旦对象带传输时延就必须在建模阶段说清楚否则 Simulink 和脚本会给出两条不一样的曲线。tf的时延用属性名挂载不要手工乘一个exp(-s*T)那样得到的模型在step里会被当成无理函数处理绘图指令会报错或者悄悄降精度。% 输入时延挂在传递函数属性上 Gd tf(5, [1 1], InputDelay, 0.2); % 离散化采样时间 0.01 秒零阶保持器 Gdz c2d(Gd, 0.01, zoh); % 检查离散前后的直流增益是否一致 disp([dcgain(Gd), dcgain(Gdz)])InputDelay和OutputDelay的区别在闭环里会体现出来输入时延可以靠前馈补偿输出时延只能靠反馈预估画开环三张图时两者形状一样但接成闭环后相位裕度差得不少。采样时间这个参数很多人随手写实际上它决定了离散 Bode 图的频率上限——奈奎斯特频率是采样率的一半超过这个范围画出来的东西没有物理意义。c2d的第三个参数zoh对应零阶保持器和 Simulink 里默认的采样保持行为一致两边对照时用同一个离散化方法才不会出现“脚本对、仿真错”的假象。3. 三张图分开画step、bode、nyquist 的关键参数模型立住之后才轮到绘图。这三个函数都有一堆默认行为默认值在演示里够用一旦要拿图去做判断就必须显式给参数。下面按“时间域—频率域—复平面”的顺序拆每一张都给出我实际会用的参数组合以及参数改了之后图会怎么变。3.1 阶跃响应时间向量决定你能看到什么step(G)不带参数时会自动选时间范围规则是让响应基本到达稳态。这个自动策略在欠阻尼系统上偶尔会截断振荡尾巴超调量算出来偏小。我一般显式给时间向量步长取系统最快时间常数的五十分之一左右既不会糊掉上升沿也不至于生成几十万个点拖慢绘图。% 显式时间向量0 到 15 秒步长 0.005 t 0:0.005:15; % 拿响应数据同时保留时间轴 [y1, t1] step(G1, t); % 定量指标超调量、上升时间、调节时间 S1 stepinfo(G1); fprintf(超调 %.2f%%上升时间 %.3f s调节时间 %.3f s\n, ... S1.Overshoot, S1.RiseTime, S1.SettlingTime); % 三个对象画在一张图上对比 figure; step(G1, G2, G3, t); grid on; legend(G1 二阶欠阻尼, G2 含零点, G3 含积分);时间向量的下界也有讲究。从 0 开始是为了看清初始时刻的斜率如果对象带时延起始那段平坦区正好用来验证时延值对不对。stepinfo的调节时间默认按 2% 误差带算做课程设计时常被要求用 5%改法是把SettlingTimeThreshold传进去默认值和你需要的口径不一致时报出来的数字会差一截。G3是积分环节阶跃输入下输出一直线性爬升stepinfo对它返回的调节时间是NaN这不是函数坏了而是这类对象本来就没有稳态。看到NaN先确认对象里是不是有1/s别急着改代码。3.2 Bode 图频率范围与 mag/phase 的输出格式bode(G)的自动选频会覆盖所有极零点的模长但对高频衰减特别快的系统低频段会被压缩得看不清。手工给频率向量是更稳的做法把所有极点、零点模长取出来向两端各扩两个数量级中间用对数等距采样。% 按极零点模长自动构造频率范围 p abs(pole(G1)); z abs(zero(G1)); allc [p; z]; allc allc(allc 0); w logspace(log10(min(allc))-2, log10(max(allc))2, 500); % 画幅频和相频 figure; bode(G1, w); grid on; % 取数值注意 mag 是 1x1xN 三维数组 [mag, phase, wout] bode(G1, w); mag squeeze(mag); phase squeeze(phase); disp([wout(1), 20*log10(mag(1))]) % 低频增益单位 dBsqueeze这一步是血泪经验。单输入单输出系统的bode输出是三维数组直接拿去plot会报维度不匹配或者画出一条莫名其妙的折线。用squeeze压成列向量之后才能自己算 -3dB 带宽、算穿越频率、做二次加工。频率范围的另一头是高频。如果对象分子阶数比分母低两阶以上高频段会以 -40dB/dec 往下掉logspace上界取太高会让图变成一片空白取太低又看不到衰减趋势。我一般先按极零点范围画一次看看曲线形状再决定要不要把上界再推一个数量级。margin是这张图的常用搭档它返回幅值裕度、相位裕度、对应的两个频率[Gm, Pm, Wcg, Wcp] margin(G1); fprintf(幅值裕度 %.2f dB相位裕度 %.2f 度\n, 20*log10(Gm), Pm);注意Gm是绝对增益不是分贝忘了取20*log10会得到一个看着像 dB 其实是倍数的数字这个坑在写报告时特别容易漏。3.3 Nyquist 图与根轨迹判断稳定性的两种视角Nyquist 图的价值在于它能直接回答“闭环稳不稳”但前提是频率范围给对。默认范围可能漏掉高频段绕向原点的部分看起来没包围 -1 点实际上包围了。% 限定频率范围避免默认选频截断 figure; nyquist(G1, {0.01, 1000}); grid on; % 根轨迹视角闭环极点随增益移动 figure; rlocus(G1); grid on; % 闭环阶跃直接看时域 Gcl feedback(G1, 1); stepinfo(Gcl)nyquist的花括号参数是频率上下限单位 rad/s。下限要低于所有极点的模长上限要高于它们否则曲线两端会被截断看起来像一条开口的弧。图上那条 -1 点是否被包围需要配合pole的实部符号一起看开环有右半平面极点时包围方向和常规情况相反只凭图判断容易得出相反结论。rlocus从另一个角度回答同一个问题——闭环极点随增益怎么跑。三个对象里G1的根轨迹从两个共轭复极点出发增益加大后向左半平面弯曲和feedback之后的阶跃响应超调量变化趋势能对上。这两种视角对照着看比单独盯着 Nyquist 图猜要可靠得多。4. 从脚本搬进 SimulinkTransfer Fcn 模块与数据回收脚本画图快但课程设计往往要求在 Simulink 里搭出同样的系统并给出波形。两边对不上是最常见的求助场景问题大多不在模型本身而在求解器配置和数据回收格式。这一章把“模块参数怎么填、模型怎么用脚本搭、仿真数据怎么回到工作区再画一遍”走完整。4.1 Transfer Fcn 模块的分子分母系数与求解器Transfer Fcn 模块的两个参数分别是 Numerator coefficients 和 Denominator coefficients填法和tf一模一样降幂向量用空格或逗号分隔。区别在于这里填的是字符串脚本里用set_param赋值时要加引号。求解器的选择直接影响曲线和理论值的贴合度。线性连续系统没有不连续点用定步长ode4最稳步长取 0.001 秒画出来的曲线和step几乎重合。默认的变步长求解器在过零点附近会自动缩步长虽然精度够但输出的时间点不均匀拿去和等间隔的理论曲线对比时会多出插值误差。% 新建模型并放置模块 mdl tf_three_demo; new_system(mdl); open_system(mdl); add_block(simulink/Sources/Step, [mdl /Step], ... Time, 0, Before, 0, After, 1); add_block(simulink/Continuous/Transfer Fcn, [mdl /G1], ... Numerator, [1], Denominator, [1 0.6 1]); add_block(simulink/Sinks/To Workspace, [mdl /y], ... VariableName, yout, SaveFormat, Structure With Time); add_block(simulink/Sinks/Scope, [mdl /Scope]); % 连线 add_line(mdl, Step/1, G1/1); add_line(mdl, G1/1, y/1); add_line(mdl, G1/1, Scope/1); % 求解器配置定步长 ode4步长 1ms set_param(mdl, StopTime, 15, ... Solver, ode4, ... FixedStep, 0.001, ... SaveFormat, Structure With Time);Time、Before、After是阶跃源模块的三个参数分别对应跳变时刻、跳变前取值、跳变后取值。做单位阶跃就把后两者设成 0 和 1。To Workspace的SaveFormat有四种Structure With Time最通用里面同时有time和signals.valuesTimeseries格式在新版本里更省事字段名是Time和Data两种都能用但同一个项目里最好统一混着写迟早会取错字段。注意Transfer Fcn 模块不允许分子阶数高于分母阶数。如果对象的分子分母同阶必须先串一个极小时间常数的一阶环节或者改用状态空间模块否则仿真直接报错而且报错信息不会告诉你问题出在阶数上。4.2 用脚本跑仿真并回收数据模型搭好之后用sim跑输出是一个Simulink.SimulationOutput对象工作区变量从里面按名字取。这一步最容易踩的坑是变量的作用域——To Workspace写到的是仿真输出对象里不是基础工作区。% 跑仿真 out sim(mdl); % 从输出对象里取回数据 yout out.yout; t_sim yout.time; y_sim yout.signals.values; % 和理论阶跃响应叠在一起 [y_ref, t_ref] step(G1, t_sim); figure; plot(t_sim, y_sim, b-, LineWidth, 1.5); hold on; plot(t_ref, y_ref, r--, LineWidth, 1.2); grid on; legend(Simulink 仿真, step 理论值); xlabel(时间 (s)); ylabel(幅值);两条曲线叠上去之后差异一般集中在三个位置起始段的斜率、峰值附近的超调量、稳态值。起始段斜率不对多半是求解器步长偏大或者阶跃时刻和采样点没对齐峰值超调量对不上但稳态一致通常是阻尼比的数值精度问题把0.6写成0.6001都能在图上看出差别稳态值不一致则要回头查直流增益dcgain(G1)和仿真末值应该严格相等。step的返回时间向量和仿真时间向量长度不同是正常的两个都是按各自的采样规则生成的。真要逐点做差得先把其中一条插值到另一条的时间轴上interp1一行就够但插值会引入额外误差做精度对比时不如直接把仿真步长设成和理论时间向量一致。4.3 三个对象批量对照单个对象对上了把三个都跑一遍才算把这条链路走通。手工改模块参数太慢用set_param在循环里换系数更省事cases { [1], [1 0.6 1], G1; [2 1], [1 3 2], G2; [5], [1 1 0], G3 }; figure; hold on; for k 1:size(cases, 1) set_param([mdl /G1], Numerator, cases{k,1}, ... Denominator, cases{k,2}); out sim(mdl); yk out.yout.signals.values; tk out.yout.time; plot(tk, yk, DisplayName, sprintf(case %d, k)); end grid on; legend(show);循环里每次sim都会重新编译模型三个对象跑下来几秒钟比手工点开模块改参数快得多。cases这个元胞数组的第三列存的是理论模型本来想拿来做自动对比实际用的时候发现仿真步长和理论时间向量对不齐自动算误差反而添乱最后还是靠肉眼叠图。这个取舍看个人习惯做批量回归时值当单次验证没必要。5. 避坑清单传递函数图对不上的五个现场下面这几条都是我在改别人模型时反复遇到的每条按现象、原因、解决三步写对着排查能省不少时间。5.1 阶跃曲线起点是斜的超调量比理论值小一截现象step画出来的曲线在 t0 附近不是从零斜率平滑上升而是先有一段接近直线的爬坡峰值也偏低。原因时间向量步长太大。自动选时间时如果系统时间常数很小采样点会跨过上升沿最快的部分画出来的折线把峰值削平了。解决显式给时间向量步长取最小时间常数的五十分之一以内。二阶系统的最小时间常数约等于1/(zeta*wn)G1算下来是 3.3 秒量级但上升沿的细节在 0.1 秒内步长给 0.005 才够。步长再小到 0.0005 也不会更准只会让文件变大。5.2 Bode 图低频段斜率不对转折点位置偏移现象幅频曲线在低频段不是预期的 -20dB/dec 或 0dB/dec转折点的横坐标和手算的极点模长对不上。原因频率范围给错。bode的自动选频在极点模长跨度大时会漏掉中间段手工给的logspace上下界如果没覆盖所有极零点曲线两端会被截断看起来像斜率变了。解决先abs(pole(G))和abs(zero(G))把模长列出来取最小值和最大值各向两端扩两个数量级。G3有个极点正好在原点模长为 0构造范围时要把它排除掉否则log10(0)直接返回-Inflogspace出来的向量全是NaN。5.3 Simulink 波形滞后于理论曲线整体右移现象两条曲线形状一致仿真那条整体晚了一小段稳态值对得上就是时间轴错位。原因阶跃源的跳变时刻和求解器采样点没对齐或者对象带了时延但 Transfer Fcn 里没配Simulink 不会自动从tf的InputDelay属性继承时延。解决阶跃源的Time参数设成 0定步长求解器的步长取时延的十分之一以下有时延就在模块后面串一个 Transport Delay延迟值填同一个数。查的时候先看阶跃源本身的输出确认它在 t0 就跳了再往后找。5.4 Nyquist 图看着没包围 -1 点闭环其实已经发散现象Nyquist 曲线绕开 -1 点但feedback(G,1)之后阶跃响应发散。原因开环有右半平面极点包围判据的方向要反过来算或者频率范围下限太高把低频段那段关键绕行截掉了。解决先pole(G)看实部有没有正值有的话判据要按广义 Nyquist 处理频率下限压到最小极点模长的十分之一以下。判稳这件事别只信图pole(feedback(G,1))打印一下闭环极点实部正的一目了然。5.5 分子分母同阶模块报错或曲线在 t0 出现冲激现象Transfer Fcn 模块一跑就报参数非法或者曲线在 t0 处有一个极高极窄的尖峰。原因分子阶数等于或高于分母阶数属于非真分式物理上对应纯微分Simulink 的连续模块不支持。解决串一个时间常数很小的一阶惯性环节1/(0.001*s1)把它变成真分式或者改用状态空间模块加D矩阵。前者会引入一点高频衰减做频域分析时要把这个附加环节的影响算进去别拿着带滤波的结果去和理论 Bode 图逐点比。6. 进阶验证用 stepinfo 与 margin 把图变成数字图看多了会有错觉觉得曲线形状对就算对了。真要做判断得把图上的特征转成数字再和解析公式对一遍。二阶系统的超调量有闭式解exp(-pi*zeta/sqrt(1-zeta^2))拿它和stepinfo的输出比误差应该在千分之一量级对不上就说明模型不是标准二阶或者时间向量精度不够。zeta_list [0.2 0.4 0.7 1.0]; t 0:0.002:20; figure; hold on; for k 1:numel(zeta_list) z zeta_list(k); G tf(1, [1 2*z 1]); [y, tt] step(G, t); plot(tt, y, DisplayName, sprintf(\\zeta%.1f, z)); S stepinfo(G); theory exp(-pi*z/sqrt(1-z^2)) * 100; % 理论超调百分比 fprintf(zeta%.1f 实测 %.2f%% 理论 %.2f%%\n, ... z, S.Overshoot, theory); end grid on; legend(show); xlabel(时间 (s)); ylabel(幅值);zeta取 1.0 时理论公式分母为零会返回NaN或者复数这是临界阻尼的边界情况代码里加个判断跳过就行。实测值在zeta接近 1 时会略大于零因为数值求解没有严格到达临界状态这个偏差是正常的别当成 bug。频率域这边用margin拿到穿越频率和稳定裕度再做一次扫参看阻尼比变化时穿越频率怎么移动w logspace(-1, 1, 300); figure; hold on; for k 1:numel(zeta_list) G tf(1, [1 2*zeta_list(k) 1]); [Gm, Pm, Wcg, Wcp] margin(G); bodemag(G, w, DisplayName, sprintf(\\zeta%.1f, zeta_list(k))); fprintf(zeta%.1f 相位裕度 %.1f 度 穿越频率 %.3f rad/s\n, ... zeta_list(k), Pm, Wcp); end grid on; legend(show);跑出来会看到相位裕度随阻尼比单调上升穿越频率基本不动——因为自然频率没变增益也没变。这个结论可以用来反查如果扫出来的穿越频率跟着阻尼比一起漂说明对象里混进了别的极点模型和你以为的不是同一个。出图归档有个小技巧exportgraphics比saveas清晰得多尤其适合把三张图拼进报告exportgraphics(gcf, bode_sweep.png, Resolution, 200);分辨率给 200 是打印和屏幕都够用的折中值再高文件会大得离谱插进文档还得压缩。图片文件名带上参数值比如bode_zeta0.2.png后面回头找图时不用一个个点开看。从那以后我每次交图前都强制走一遍“三图叠一组、数字对一遍”的流程先stepinfo和解析解比再margin和手算的穿越频率比两个都对上才敢把图放进报告。图好看不等于对这是被返工逼出来的习惯。希望帮到你。本文还有配套的精品资源点击获取