简介:面向初学者的倒立摆模糊控制Simulink仿真学习包,围绕非线性倒立摆的建模、控制器设计与稳定性验证展开,适合智能控制课程实验、课程设计及模糊逻辑入门实践。压缩包共140个文件、约6.15MB,包含mat数据、m脚本、mdl仿真模型、fis模糊规则、avi仿真录屏以及png/jpg图示和docx实验报告等,便于从模型搭建、规则调试到结果观察形成闭环。已有6460人学习。资源涵盖角度模糊PID控制、位置与角度分段模糊控制两阶段实验,并提供原始与改进仿真对比、控制策略设计说明、仿真结果分析及动画演示脚本,可帮助读者理解模糊化、规则推理与去模糊化的实现过程,并掌握Simulink环境下倒立摆联合控制的调试与复现方法。
1. 先别被名字吓住:这三样东西各是什么
第一次看到「倒立摆的模糊控制(基于 Simulink 仿真)」这串字,大多数零基础的人脑子里会同时冒出三个问号:倒立摆是个什么装置?模糊控制是不是「模模糊糊地控制」?Simulink 又是个什么东西,听着像 MATLAB 里的一个附赠小工具。其实把它们拆开,都能用生活场景讲明白。倒立摆就是用手掌托住一根倒立的拖把杆,靠不停微调手掌的位置让它别倒;模糊控制就是不写那一长串公式,而是像老司机一样用「偏得太多了」「快倒了」「轻轻往右推一点」这类经验句子来决定怎么推;Simulink 则是把你脑子里「偏了就推、推了会倒」这套关系画成一张会动的框图,让 MATLAB 在后台帮你一秒一秒算下去。这份资源就是把这三样串起来:用 MATLAB 里的 Simulink 和模糊控制工具箱,先把一个模糊控制器搭出来,再把它接到倒立摆模型上跑仿真,全程对着图形界面操作,适合刚学完 MATLAB 基础语法、想要一个看得见摸得着的控制入门例子的人。
2. 为什么新手值得从倒立摆和模糊控制入手
很多零基础的人卡在同一个地方:MATLAB 的语法学完了,if、for、矩阵运算都会写,可是一到「用它做点真东西」就无从下手。网上的控制理论课程一上来就是传递函数、状态空间、极点配置,符号一多,人直接劝退。倒立摆配模糊控制这个组合,恰好是绕开那道高墙的一条小路——你不需要先把控制理论啃透,也能看到一个闭环系统在自己手里动起来。
2.1 倒立摆为什么是控制领域的「练手木桩」
倒立摆的结构简单到一句话能说完:一根杆子立在一个可以左右移动的小车上,杆子会倒,你要靠移动小车把它扶住。就这一个小装置,几乎把控制入门要遇到的所有难处都集齐了。
第一,它开环不稳定。你把杆子扶到竖直位置松手,它一定会倒,不存在「放着不管它自己稳住」的可能。这一点很关键,因为像温度控制、水位控制这类对象,你什么都不做它至少不会爆炸,而倒立摆只要你控制器没接好、参数设错,曲线会在零点几秒内飙到天上。这种「立刻给你反馈」的特性,对新手其实是好事——你的错误藏不住,改完马上能看到区别。
第二,它是欠驱动的。小车只有一个左右方向的推力,却要同时管住小车的位置和杆子的角度两个量,一个输入管两个输出,不可能随便分配。很多新手第一次接触「一个输入、多个输出」时会懵,倒立摆用最直观的方式让你接受这件事。
第三,它天然带着非线性。真实世界里杆子倒下角度大了,那套「小角度近似」的线性公式就不准了。这正好是模糊控制能发挥的地方——你不需要一个精确到小数点后三位的数学模型,只需要知道「角度大就往回使劲推」这条经验。
我第一次接触倒立摆的时候,花了整整一个下午才让曲线勉强收敛,中间反复怀疑是不是自己公式抄错了。后来才明白,卡住我的不是公式,而是没搞清楚「控制器输出到底接到哪个物理量」。这个坑,等你动手的时候也一定会遇到,所以后面专门留了一章讲。
从学习收益上看,倒立摆这一个模型能同时练到四件事:怎么把一个物理系统翻译成 Simulink 框图、怎么设计一个控制器、怎么判断仿真结果是好是坏、怎么调参数。这四件事换成任何一个控制项目都是通用的,花时间在这上面不亏。
2.2 模糊控制解决的是什么问题:不用精确公式也能干活
传统控制里最常听到的是 PID,它的思路很直接:算出「目标值减去当前值」的误差,误差大了使劲调,误差小了轻点调。道理简单,可它依赖一个前提——你得知道这个系统大概长什么样,才好定那几个系数。倒立摆这种不稳定对象,PID 也能做,但你得先有一套靠谱的数学模型,还得把参数调到刚好,对零基础的人来说门槛不低。
模糊控制换了个思路:不追求精确公式,改用「人话规则」。你想想自己用手扶拖把杆的时候,脑子里会算微分方程吗?不会。你只会想「歪得有点多了,得赶紧往反方向多推一点」「已经快正了,轻轻带一下就行」。模糊控制就是把这套人话翻译成机器能执行的东西,中间经过三步。
第一步是模糊化。你对着控制器说「角度偏大」,机器听不懂「偏大」这个词。所以要给角度划几个区间,比如「负大、负小、零、正小、正大」,每个区间用一个隶属函数描述。隶属函数听着玄乎,其实就是一条三角形或者梯形的小曲线,横轴是角度值,纵轴是「这个角度属于『正大』的程度」,0 表示完全不属于,1 表示完全属于。角度是 0.25 的时候,它可能同时算 0.6 的「正小」和 0.2 的「正大」,不冲突,这就是「模糊」这个词的由来——不是模模糊糊不准确,而是允许一个值同时部分地属于多个类别。
第二步是规则推理。把经验写成一句一句的规则:「如果角度是正大,那么推力是正大」「如果角度是零,那么推力是零」。这些规则凑在一起就是一张规则表,表的行是输入的语言值,列是输出的语言值。规则数量取决于你给每个变量分几个语言值,分五个就是五条对角线规则,分七个就是七条,一般不复杂。
第三步是去模糊化。推理出来的结果还是一堆「隶属度」,得把它变成一个具体的数字,比如「这一时刻推力应该是 -1.8 牛顿」。最常用的方法是重心法,把所有激活的规则结果叠在一起,算这块面积的几何中心。这一步 Simulink 里的模糊控制器模块会自动帮你算,你不用手推。
模糊控制真正好用的场景有三个特征:对象模型难建、有明显非线性、有经验丰富的操作者但说不出精确公式。倒立摆三条全中。它也不是万能的,精度要求极高、需要严格稳定性证明的场合,还是得回到经典控制方法。入门阶段先把「能用规则把系统稳住」这件事跑通,建立起直觉,比纠结哪种方法更高级有价值得多。
2.3 Simulink 的价值:把一页公式变成一张能跑的图
如果你只用 MATLAB 写脚本做仿真,大致要这么干:定义一堆变量、写一个 for 循环代表时间推进、每一步算一遍微分、把结果存进数组、最后画图。这套流程写一次还行,改一次参数就得重跑一遍脚本,中间哪一步错了,报错信息往往指向一个你根本没注意的数组下标。
Simulink 把这套东西变成了图。左边一个方块代表输入信号,中间一个方块代表控制器,右边一个方块代表被控对象,方块之间用箭头连起来,箭头的方向就是信号的流向。你想改参数,双击方块弹个对话框,改完点运行,曲线直接出现在示波器窗口里。这套方式对零基础的人最友好的一点是:你脑子里想的「谁影响谁」,和屏幕上画的「谁连着谁」,是一一对应的。想不明白的时候盯着框图看,比盯着代码看有效得多。
Simulink 背后还有一层东西叫求解器,负责决定「一秒钟分成多少步来算」。常听到的 ode45、ode23 就是不同求解器的名字。入门阶段不用深究它们的数学原理,只要知道:默认的变步长求解器在大多数情况下够用,如果遇到仿真卡住或者报步长过小的警告,换一个定步长求解器往往能解决。这件事在第 5 章的排查清单里还会再提一次。
3. 动手前的准备:把工具认全,把界面跑熟
在真正碰倒立摆之前,有两件事得先办妥:确认你电脑上的 MATLAB 装齐了要用的组件,以及熟悉 Simulink 界面里那几个真正用得上的地方。很多人卡在第一步就放弃了——不是学不会,是打开软件看到密密麻麻的按钮,不知道从哪下手。
3.1 三个名词分清楚:MATLAB、Simulink、模糊逻辑工具箱
这三个名字经常被混着说,其实是三层关系。
MATLAB 是底座,是一个以矩阵运算为核心的计算环境,命令行窗口、工作区、脚本编辑器都属于它。你写的每一行代码、定义的每一个变量,最终都活在 MATLAB 的工作区里。
Simulink 是长在 MATLAB 上面的图形化仿真环境。它不是一个独立软件,你没法脱离 MATLAB 单独打开它,启动方式是在 MATLAB 命令行里敲simulink,或者点工具栏上那个带三个方块的图标。它负责的是「搭框图、跑仿真、看波形」。
模糊逻辑工具箱是一个附加组件,提供的是模糊控制器相关的函数和模块。Simulink 里那个叫 Fuzzy Logic Controller 的方块,就是这个工具箱提供的。如果你只装了 MATLAB 主程序,这个方块在库浏览器里是找不到的。
所以第一步,先确认工具箱在不在。在 MATLAB 命令行里敲这一行:
% 列出当前安装里所有可用的工具箱和它们的基本信息 ver敲完回车,命令行会打印出一长串清单,每一项是一个已安装的产品名加版本号。你要做的是在这串清单里找有没有包含「Fuzzy Logic」字样的条目。找到就说明齐了,可以直接往下走;找不到,就得回头把工具箱补装上,否则后面拖不出模糊控制器模块,会白白浪费半天时间。
提示:如果暂时装不上工具箱,也不是完全没法继续。Simulink 里有一个 Fuzzy Logic Controller 的替代方案,是用 MATLAB Function 模块把模糊推理手写进去,但那是进阶内容,零基础阶段不建议一上来就绕这条路。先把工具箱装齐,省下来的时间远大于折腾替代方案的时间。
3.2 第一次打开 Simulink,只需要认识五个地方
打开 Simulink 之后弹出的界面信息量很大,新手很容易被吓到。其实入门阶段真正高频用到的只有五处,其他的可以先当它们不存在。
第一处是新建模型入口。在 Simulink 起始页上有一个 Blank Model 的选项,点它就会打开一个空的画布。这块画布就是你后面所有工作的地方,你的倒立摆模型、控制器、示波器全都会摆在这上面。
第二处是库浏览器。画布左边或者工具栏上有一个叫 Library Browser 的按钮,点开会弹出一个树形目录,里面按类别摆着所有可用的模块:Sources 下面有信号源,Sinks 下面有示波器,Continuous 下面有积分器,Math Operations 下面有求和和增益。你要用的模块全部从这里拖到画布上。刚开始不熟的时候,可以直接在搜索框里敲模块英文名,比一层层翻目录快得多。
第三处是模块参数对话框。画布上每个方块双击一下,都会弹出一个窗口让你填参数。比如积分器可以设初始值,增益可以设倍数,求和模块可以设正负号。绝大部分「模型跑不对」的问题,根子都在这里某一项填错了。
第四处是示波器 Scope。这是一个专门用来画波形的模块,从 Sinks 里拖出来,把想看的信号线连上去,运行完双击它就能看到曲线。新手最常见的操作失误是忘了接示波器,跑完仿真发现什么都没有,还以为模型没跑起来。
第五处是工具栏上的运行按钮和仿真时长。运行按钮是一个三角形播放图标,旁边的输入框写着仿真的停止时间,默认可能是 10.0。这个数字的单位是秒,改它等于改仿真的总时长。想让系统多跑一会儿看长期趋势,就把它改大。
把这五处摸熟,剩下的按钮一年也用不上几次。
3.3 从零搭一个最小模型:让一个信号动起来
在碰倒立摆之前,先花十分钟搭一个「什么也不控制、纯粹看看信号怎么流动」的模型。这一步的目的是让你在没有控制概念干扰的情况下,把「拖模块、连线、改参数、运行、看波形」这套动作走一遍。
具体步骤是这样的:
- 新建一个空白模型,点工具栏上的保存按钮,给它起个名字存到当前工作文件夹里。
- 打开库浏览器,搜索 Sine Wave,把它拖到画布左侧。
- 再搜索 Gain,拖到画布中间。
- 再搜索 Scope,拖到画布右侧。
- 鼠标从 Sine Wave 右侧的小三角拖一条线到 Gain 左侧的入口,再从 Gain 右侧拖一条线到 Scope 的入口。
- 双击 Gain,把 Gain 的值从默认的 1 改成 2。
- 双击 Sine Wave,看一眼里面的 Amplitude 是 1、Frequency 是 1,保持默认。
- 把工具栏上的停止时间改成 5,点运行按钮。
- 双击 Scope,在弹出的波形窗口里点一下自动缩放按钮,你就能看到一条振幅为 2 的正弦曲线。
这一套动作做完,你已经走完了一次完整的仿真循环:信号从源头产生,经过一个模块处理,最后被记录下来。理解这个流程比理解任何一个具体模块都重要——后面所有的倒立摆模型,本质上都是这条线变复杂了而已。
如果你习惯用命令行,这套操作也能用代码完成,效果完全一样:
% 新建一个空白模型并立刻保存到当前工作目录 mdl = 'firstModel'; new_system(mdl); % 在内存里创建一个空模型 open_system(mdl); % 在界面上打开它 save_system(mdl, fullfile(pwd, [mdl '.slx'])); % 存成 slx 文件这里new_system的括号里填的是模型的内部名字,open_system负责把它显示出来,save_system的第二个参数是完整路径,fullfile和pwd组合起来就是「当前工作文件夹下的这个文件名」。零基础阶段我更推荐你用鼠标拖,因为拖的过程中你会看到模块长什么样、有哪些接口,这些信息对后面自己搭模型很有帮助。
4. 第一个模糊控制器:从规则表到能跑的闭环
前面铺垫了那么多,现在开始干正事。这一章的目标很明确:先手工搭出一个模糊控制器,再把它接进一个简单的闭环里,让你亲眼看到「控制器在干活」。这个闭环第一次不接倒立摆,而是接一个最听话的一阶对象,因为它几乎不可能失败,能给你一个明确的成功信号。等这个跑通了,再把同一个控制器换到倒立摆上。
4.1 先把输入输出定下来:控制器到底吃进什么、吐出什么
动手之前必须先在纸上(或者脑子里)回答两个问题:这个控制器接收什么信号,输出什么信号。这一步想不清楚,后面建模一定翻车。
对于倒立摆,控制器的输入是「当前角度和目标角度的偏差」,输出是「给小车施加的推力或者加速度」。目标角度是 0,也就是杆子竖直向上,所以偏差就等于负的当前角度。输出推力的正负方向,取决于你怎么定义坐标轴——这一句话现在看着抽象,等你在第 4.4 节跑出第一条直线飞出去的曲线时,就会回来重新读这一段。
再确定取值范围。这一步决定了后面模糊集怎么划。假设摆杆角度在正负 1 弧度以内(大约正负 57 度),超出这个范围基本已经倒了,控制也没意义,所以输入偏差的论域定在 [-1, 1]。输出推力假设最大能到正负 2,论域定在 [-2, 2]。这两个数字不是标准答案,你可以按自己模型的实际情况改,但入门阶段先用这两个值,别一上来就纠结取多少合适。
接着划分语言值。分出五个档:负大(NB)、负小(NS)、零(Z)、正小(PS)、正大(PB)。五个档是入门最常用的数量,三个太粗控不住,七个规则条数翻倍对新手负担太重。
最后是规则。既然只有一个输入,规则就是一对一映射:偏差是负大,输出就是负大;偏差是零,输出就是零。规则表看着像废话,但它是整个控制器逻辑的骨架,加输入之后规则才会真正变复杂。
4.2 用模糊推理设计器搭出控制器
有两种做法:图形界面点,或者命令行写。图形界面对新手更直观,但如果想先把整个结构看清楚,命令行反而更清晰,因为每一行在干什么一目了然。
先看命令行的版本,把下面这段敲进 MATLAB 的脚本文件或者命令行里,一次执行:
% 建一个空的 Mamdani 型模糊控制器,名字随便起 fis = mamfis('Name','firstFuzzy'); % 输入变量:角度偏差 e,论域 -1 到 1 fis = addInput(fis,[-1 1],'Name','e'); fis = addMF(fis,'e','trimf',[-1 -1 -0.4],'Name','NB'); % 负大 fis = addMF(fis,'e','trimf',[-0.7 -0.3 0 ],'Name','NS'); % 负小 fis = addMF(fis,'e','trimf',[-0.2 0 0.2],'Name','Z'); % 零 fis = addMF(fis,'e','trimf',[ 0 0.3 0.7],'Name','PS'); % 正小 fis = addMF(fis,'e','trimf',[ 0.4 1 1 ],'Name','PB'); % 正大 % 输出变量:推力 u,论域 -2 到 2 fis = addOutput(fis,[-2 2],'Name','u'); fis = addMF(fis,'u','trimf',[-2 -2 -0.8],'Name','NB'); fis = addMF(fis,'u','trimf',[-1.4 -0.6 0 ],'Name','NS'); fis = addMF(fis,'u','trimf',[-0.4 0 0.4],'Name','Z'); fis = addMF(fis,'u','trimf',[ 0 0.6 1.4],'Name','PS'); fis = addMF(fis,'u','trimf',[ 0.8 2 2 ],'Name','PB'); % 五条规则:输入是哪个档,输出就走对应的档 ruleList = [ 1 1 1 1; % e 是 NB -> u 是 NB 2 2 1 1; % e 是 NS -> u 是 NS 3 3 1 1; % e 是 Z -> u 是 Z 4 4 1 1; % e 是 PS -> u 是 PS 5 5 1 1; % e 是 PB -> u 是 PB ]; fis = addRule(fis, ruleList); % 把控制器画出来看看规则曲面长什么样 figure; plotmf(fis,'input',1); % 看输入变量的隶属函数形状 figure; gensurf(fis); % 看「输入怎么映射到输出」的整体曲面这段代码里有几个参数值得解释清楚。trimf表示三角形隶属函数,后面三个数字分别是三角形的左端点、峰值点和右端点。以输入的第一个隶属函数[-1 -1 -0.4]为例,它的意思是在角度偏差等于 -1 的时候隶属度是 1,一直保持到 -1(也就是左边是个斜坡直上),然后线性下降到 -0.4 的位置变成 0。这就是一条左半边贴边的三角形,代表「负得很大」这个语言值。
addRule里每一行四个数字的含义是:第一个是输入变量的语言值序号(1 对应 NB,2 对应 NS,以此类推),第二个是输出变量的语言值序号,第三个是规则权重,入门阶段一律填 1,第四个是连接方式,1 表示 and、2 表示 or,单输入的情况下填哪个都不影响结果。
gensurf画出来的那张图非常值得多看两眼。横轴是输入,纵轴是输出,你会看到一条从左上到右下的斜线,中间略微弯曲。这条线就是你的控制器全部的行为——输入多少,输出多少,一目了然。新手常见的误区是觉得模糊控制是个黑匣子,其实画出来就这么简单。
如果你更想用图形界面,在命令行敲fuzzyLogicDesigner会打开一个可视化编辑器,左边加变量,中间画隶属函数,下面填规则,改完点保存。两条路的效果一样,命令行版本的好处是可复现、能存档、改参数方便,建议至少在入门阶段用命令行把结构过一遍。
注意:上面用的是较新版本才有的
mamfis这类函数式写法。如果你的 MATLAB 版本比较老,敲第一行就报「未定义函数」,那就改用newfis、addvar、addmf、addrule这套老函数,功能完全对应,只是参数顺序不同。或者干脆走图形界面,那个没有版本兼容问题。
4.3 把控制器接进闭环:先控一阶对象,再上倒立摆
控制器搭好了,它现在只是一段数学关系,还没跟任何东西连起来。要看到它在干活,必须把它放进一个闭环里:控制器的输出作用到对象上,对象的输出再反馈回控制器。
第一个闭环选一个最简单的对象,用式子写就是「变化率等于负的当前值加上控制量」,也就是y' = -y + u。这个对象的脾气是:没人管它的时候,它会自己慢慢回到 0;你推它,它能加速回到 0。它稳定、听话,几乎不可能失控,正好用来验证控制器有没有接对。
在 Simulink 里搭这个闭环,按下面十步走:
- 新建一个空白模型,存成
fuzzyLoop.slx。 - 从库浏览器拖一个 Integrator 到画布中间偏右的位置,双击它,把 Initial condition 改成 1。这一步设的是输出的初始值,等于说「一开始系统偏离目标 1 个单位」。
- 拖一个 Gain 到画布中间,双击把 Gain 改成 -1。这个负号就是「取误差」的动作,输出信号乘 -1 得到偏差。
- 拖一个 Sum 模块,双击把符号列表设成
++,两个加号表示两路输入都是加。 - 拖一个 Fuzzy Logic Controller 模块到画布上,双击它,在 FIS name 那一栏填
firstFuzzy,这个名字必须和你工作区里的变量名一字不差。 - 从 Integrator 的输出端拖线到 Gain 的输入端,再从 Gain 的输出端分两路:一路连到 Fuzzy Logic Controller 的输入口,另一路连到 Sum 的第一个加号输入。
- 从 Fuzzy Logic Controller 的输出端拖线到 Sum 的第二个加号输入。
- 从 Sum 的输出端拖线回到 Integrator 的输入口。这一步形成闭环,线会绕回来,很正常。
- 再拖一个 Scope,把 Integrator 的输出也接一份进去,这样你才能看到结果。
- 把停止时间设成 10,点运行。
跑完双击 Scope,你应该看到一条从 1 出发、先下降、可能在 0 附近稍微冲过头一点点、然后稳稳贴在 0 附近的曲线。这条曲线的意思是:系统一开始偏了 1 个单位,模糊控制器一口气给了接近满额的负向推力,把偏差快速压下去,等到接近目标时推力自动变小,最后稳稳停住。这就是模糊控制器在起作用。
想让整个过程可重复,可以在命令行里控制仿真:
% 把控制器变量确保送进工作区,然后设置仿真参数再运行 assignin('base','firstFuzzy',fis); % 把控制器对象放进基础工作区 set_param('fuzzyLoop','StopTime','10'); % 仿真 10 秒 set_param('fuzzyLoop','Solver','ode45'); % 指定变步长求解器 out = sim('fuzzyLoop'); % 运行,结果打包进 out 变量assignin那一行的作用是把工作区里的fis变量用firstFuzzy这个名字再放一份到基础工作区,因为 Simulink 模型运行时只认基础工作区里的变量名。这一步是新手最容易漏的:明明脚本里建好了控制器,一运行就报「找不到变量 firstFuzzy」,八成就是名字没对上或者没放进基础工作区。
sim函数返回的out是一个仿真结果对象,里面装着所有记录下来的信号。想直接画图,可以这样:
% 从仿真结果里取出输出信号并画出来 plot(out.yout); % 新版里信号默认记录在 yout 里 grid on; xlabel('时间 (s)'); ylabel('系统输出');如果提示yout不存在,说明你的模型里没有把信号勾选成「记录」,去模型里点一下那条线,在信号属性里勾上记录就行,或者在 Scope 里直接看也一样。
4.4 换成倒立摆:同一个控制器,换一个对象
一阶对象跑通了,说明控制器的搭建和接线都没问题。现在把对象换成一个简化的倒立摆模型,看看同一个控制器能不能对付更难的家伙。
倒立摆的真实动力学比较复杂,入门阶段先用一个小角度下的简化版本,用式子写就是「角加速度等于一个正比于角度的项,加上一个正比于推力的项,再减去一点阻尼」。翻译成框图就是三个部分:角度经过一个增益回到求和点、角速度经过一个负增益回到求和点、控制器输出经过一个增益进入求和点,求和结果接两级积分器串起来,第一级积分得到角速度,第二级积分得到角度。
搭法步骤如下:
- 在刚才那个模型的基础上另存一份,改名叫
pendulumLoop.slx,把原来那个 Integrator 和 Gain 的连线全删掉。 - 拖两个 Integrator 出来,第一个的输出接第二个的输入。双击第一个,初始值设 0;双击第二个,初始值设 0.2,代表一开始杆子歪了 0.2 弧度。
- 拖一个 Sum 模块,符号设成
++-,三个输入口。 - 从第二个积分器(角度)的输出拉一条线,经过一个 Gain 值为 4 的模块,接进 Sum 的第一个加号。
- 从第一个积分器(角速度)的输出拉一条线,经过一个 Gain 值为 -0.8 的模块,接进 Sum 的第二个加号。
- 从模糊控制器的输出拉一条线,经过一个 Gain 值为 2 的模块,接进 Sum 的第三个减号口。
- 把 Sum 的输出接到第一个积分器的输入口,闭环形成。
- 角度的反馈信号还要接一份到那个 Gain 为 -1 的模块,再进模糊控制器,作为偏差输入。
- 加一个 Scope,把角度信号接进去。
- 停止时间设成 10,运行。
这里的几个数字需要说明一下:那个 4 对应的是「重力除以摆长」这一类系数,-0.8 是人为加的阻尼,2 是推力到角加速度的转换系数。这三个数不是从哪本教材上抄来的标准值,是为了让入门阶段的曲线好看、容易收敛而选的一组示意值。等你跑通了,完全可以换成自己推出来的真实参数,那时候曲线会难看很多,但也更接近真实。
跑完看波形,结果大概率不如一阶对象那么漂亮。可能是一条先往下走、抖几下、慢慢贴到 0 附近的曲线,也可能是幅度越来越小的振荡,甚至可能是抖一阵之后朝一边跑掉。这三种结果都是正常现象,不需要怀疑自己搭错了。原因是这个控制器只有一个输入,只看到了角度,没看到角速度,相当于闭着眼睛扶杆子,只能凭「歪了多少」判断,感觉不到「正在以多快的速度倒下去」。要真正稳住倒立摆,得把角速度也作为第二个输入,规则从五条变成二十五条,那是下一阶段的事。
现在这一步的价值在于:你亲眼看到了控制器在跟一个不稳定的对象打交道,而且能读懂曲线为什么长这样。这比曲线本身漂亮不漂亮重要得多。
5. 新手最容易翻车的五个地方:排查清单
这一章是把常见故障集中列一遍。下面这五条,每一条都有明确的「现象、原因、解决」三段,遇到问题时按顺序对一遍,能省下大量瞎猜的时间。
5.1 建模和接线阶段的三个坑
坑一:运行就报「找不到变量 firstFuzzy」。
现象是点运行按钮之后,命令行弹出红色报错,说某个变量未定义,仿真根本没开始跑。原因通常是两种:要么你在脚本里创建的控制器变量名和模块里填的名字不一致,比如脚本里叫fis、模块里填firstFuzzy;要么变量只存在于脚本的局部作用域里,没被送进 Simulink 认的基础工作区。解决办法很直接:确认模块 FIS name 那栏填的名字,然后在命令行里用assignin('base','firstFuzzy',fis)明确送一份进去,再运行。养成习惯之后,每次跑仿真前都先看一眼工作区里有没有这个名字,就不会再犯。
坑二:曲线是一条直线冲出屏幕。
现象是波形刚开始还正常,然后迅速朝一个方向飙走,几秒钟就到极大值,Scope 的纵轴被拉伸得看不清细节。原因多半是符号反了:控制器的输出方向和被控对象的响应方向没对上,本来该往左推的结果往右推了。解决办法是先把控制器输出乘一个 -1 试试,也就是在控制器和对象之间插一个 Gain 值为 -1 的模块,重新跑一遍。如果曲线从发散变成收敛,就是符号问题;如果还是发散,那就要检查对象模型里那几个增益的正负号。我在这上面栽过不止一次,后来养成一个习惯:搭完闭环第一件事不是调参数,而是先跑两秒确认方向对不对。
坑三:控制器输出一直是零,系统像没人管一样自己漂。
现象是波形和「断开控制器、让对象自由演化」几乎一样,看不出任何控制作用。原因是输入信号超出了模糊控制器的论域范围。比如你把输入论域设成 [-1, 1],而实际角度偏差已经到了 1.5,这时候所有隶属函数的隶属度都是 0,没有任何规则被激活,去模糊化算出来的结果就是 0。解决办法是打开 Scope 看一眼输入信号的实际幅值,如果确实超了,就把论域放大,比如改成 [-2, 2],同时把隶属函数的端点也按比例铺开。这个坑的隐蔽之处在于它不报错,只是安静地不干活。
5.2 仿真运行和看波形阶段的两个坑
坑四:仿真跑到一半卡住,或者弹出「步长过小」的警告。
现象是点运行之后进度条长时间不动,最后弹一个警告说在某个时刻无法满足误差容限。原因是默认的变步长求解器在处理不连续或者快速变化的信号时,会不断缩小步长试图满足精度要求,缩到极限就卡住了。解决办法有两个:一是把求解器从默认的变步长换成定步长,在模型配置里把 Solver 改成 Fixed-step,步长设 0.01;二是在容易出现突变的信号后面加一个一阶滤波,把尖锐的跳变磨圆。入门阶段推荐第一种,改一个选项就好,不用动模型结构。
坑五:库浏览器里根本搜不到 Fuzzy Logic Controller 模块。
现象是在库浏览器搜索框里敲 Fuzzy,什么也搜不出来,或者拖出来的模块是灰的不能双击。原因就是第 3.1 节提到的,模糊逻辑工具箱没装。解决办法是先跑一次ver确认,如果清单里确实没有,就去补装;如果清单里有但模块还是搜不到,检查一下是不是把 Simulink 的库浏览器和 MATLAB 的路径搞混了,重启一次 MATLAB 通常能解决。这一条之所以放在最后,是因为它一旦发生就没法绕过,越早确认越好,别等到模型搭了一半才发现。
把这五条打印出来贴在显示器边上,遇到问题时先对一遍,能过滤掉入门阶段八成的挫败感。剩下两成,多半是参数没调好,那就是耐心活了。
6. 下一步学什么:从「能跑」到「跑得住」的三步路
第一个闭环能跑通之后,很多人会陷入一个新的迷茫:模型是跑起来了,可下一步该往哪走?这里给一条我自己走过、也带过别人走过的路径,分三步,每一步都有明确的完成标志。
第一步,把单输入改成双输入。这是从「玩具」走向「能稳住」的关键一跳。给模糊控制器加第二个输入,也就是角速度,论域按你模型里角速度的实际范围定,同样划五个语言值。规则从五条变成二十五条,看着多,其实有规律:角度和角速度同号时使劲推,异号时轻推或者不推,这就是人在扶杆子时的真实策略。规则表可以按这个思路自己填,填完用gensurf看一眼三维曲面,正常的曲面应该是一个平滑的斜面,如果出现明显的坑或者平台,说明某几条规则写反了。
| 阶段 | 控制器输入 | 规则条数 | 判断成功的标准 |
|---|---|---|---|
| 第一步 | 角度偏差 | 5 条 | 一阶对象能收敛到 0 附近 |
| 第二步 | 角度偏差 + 角速度 | 25 条 | 倒立摆能从 0.2 弧度回到 0 附近不振荡 |
| 第三步 | 同上,加输出缩放系数 | 25 条 | 抗干扰:中途给一个扰动还能回正 |
第二步,学会调输出缩放系数。很多人的双输入控制器规则写得没问题,就是曲线一直在目标值附近小幅抖动,怎么改规则都没用。这时候问题往往不在规则,而在输出的整体力度:力度太小推不动,力度太大冲过头。解决办法是在控制器输出后面串一个 Gain,从 1 开始,先试 0.5 再试 2,看哪一个抖动最小。这个系数没有理论最优值,只能试,试的过程本身就是建立手感的过程。
第三步,加扰动测试鲁棒性。曲线能收敛不等于控制器好用,真实系统总有干扰。在仿真跑到第 5 秒的时候,给角度叠加一个短时脉冲,看系统能不能自己恢复。如果恢复得很慢或者干脆跑飞,说明规则覆盖得不够,需要补几条针对大偏差的规则。
还有一件事值得早点做:把每次调参的结果存下来。文件名带上关键参数,比如pendulum_k2_d08.slx,旁边配一个文本文件记录这次改了什么、曲线长什么样。我最早做仿真的时候嫌麻烦不存,结果调出一个不错的参数之后手滑覆盖了模型,那一版再也找不回来,只能从头再调一遍。从那以后我每次改完参数、看到一条满意的曲线,都会强制走一遍「另存 + 记一行备注」的流程,哪怕
本文还有配套的精品资源,点击获取