简介:倒立摆是控制理论中的经典非线性系统,这份资料面向需要掌握MATLAB/Simulink建模与控制器设计的工科学生、控制方向初学者或相关工程人员,提供了从数学模型、控制器设计到仿真设置的一整套入门实践。资源包共9个文件,包括MATLAB脚本(.m)、Simulink模型(.slx/.mdl)、说明文档(.doc/.ppt)等,涵盖PID、LQR等常见控制策略的实现与仿真。包体大小372KB,结构精简但功能完整。目前已有2343人浏览学习。通过该包可直观看到倒立摆状态方程如何转化为Simulink模块,了解控制器子系统如何与植物模型连接,并参考文档中的理论解析与实际仿真结果,快速搭建自己的倒立摆稳定控制实验。对于想从理论走向仿真验证的学习者,是一份不错的参考资料。 倒立摆这玩意儿,控制领域的人多少都跟它打过交道。我前前后后用Simulink搭过不下三次,每次感受都不一样:第一次只顾着把模型跑起来,结果是假的稳定;第二次开始思考控制器为什么这样设计;第三次才真正体会到从物理方程到仿真模型再到控制算法,整个链路里每一环都是坑,也都是学问。如果把经典控制理论、现代控制理论和MATLAB/Simulink工具链串成一条线,倒立摆仿真就是最好的练习场。
这篇文章把我完整的建模与仿真过程记录下来,包括数学推导、两种Simulink建模路线的对比、PID与LQR控制器的取舍,以及我在调试过程中踩过的一堆坑。适合正在做自动控制实验、学现代控制理论,或者想通过一个具体项目把Simulink用熟练的读者参考。
1. 建模仿真前必须搞清楚的物理方程
很多人拿到倒立摆第一件事就是打开Simulink开始拖模块,这是最要命的习惯。仿真跑起来容易,跑得对不对完全是另一回事。你一定要先能在纸上把运动方程写出来,哪怕最后只是在MATLAB里定义一个矩阵,也比你闷头拖几个积分器靠谱得多。
1.1 从拉格朗日方程到运动微分方程
倒立摆的物理模型通常简化为一辆小车加一根可绕铰链旋转的摆杆。定义小车质量为M,摆杆质量为m,摆杆重心到铰链距离为l,摆杆绕重心转动惯量为I,小车水平位移为x,摆杆相对竖直向上方向的夹角为θ,水平控制力为F。
用拉格朗日方程推导最省事,拉格朗日量L等于系统动能T减去势能V。小车的动能是(1/2)M ẋ²,摆杆的动能包括质心平动动能和绕质心的转动动能,质心位置可以写成(x + l sinθ, l cosθ)。算完化简后得到的非线性运动方程组是:
(M + m)ẍ - mlθ̈cosθ + mlθ̇²sinθ = F
mlẍcosθ + (I + ml²)θ̈ - mglsinθ = 0
这里要强调一点,θ的定义是摆杆与竖直方向的夹角,θ=0对应摆杆稳稳定定立在正上方。如果你习惯把θ定义成与水平方向的夹角,后面所有方程的符号都会变,仿真出来的现象会让你怀疑人生。
1.2 线性化与状态空间形式
真实倒立摆是非线性系统,但控制器设计通常先在线性化模型上做,因为线性系统那一套状态空间、极点配置、LQR理论都能直接用。在θ=0附近做小角度假设:sinθ≈θ,cosθ≈1,并忽略θ̇²这样的高阶小量,上面两个方程化简为:
(M + m)ẍ - mlθ̈ = F
mlẍ - (I + ml²)θ̈ = mglθ
这里有个符号细节值得说一说。第二行方程里θ的符号是正的,意味着摆杆向右偏(θ>0)时,如果小车没有加速度,角加速度方向是让它继续倒下去,这正是倒立摆“不稳定”的数学体现。很多初学者在这里符号搞反,结果仿真出来发现系统开环居然是稳定的,那才叫诡异。
联立两个方程,消去中间变量后,把状态向量设为X = [x, ẋ, θ, θ̇]ᵀ,控制输入u = F,就能写出标准的线性状态空间方程:
Ẋ = AX + Bu
其中:
A = [[0, 1, 0, 0], [0, 0, -(m²gl²) / (M(M+m)l² + MI), 0], [0, 0, 0, 1], [0, 0, mgl(M+m) / (M(M+m)l² + MI), 0]]
B = [[0], [(I + ml²) / (M(I + ml²) + Mml²)], [0], [ml / (M(I + ml²) + Mml²)]]
实际编程时别手算这些分式,直接在MATLAB里用符号推导或者数值代入都行,只要保证矩阵与系统方程对应正确即可。
1.3 仿真参数的选定
参数随便乱设会导致后面控制器设计无所适从。我用过一组比较接近真实实验台的数据:
- M = 0.5 kg(小车质量)
- m = 0.2 kg(摆杆质量)
- l = 0.3 m(摆杆重心到铰链距离)
- I = 0.006 kg·m²(摆杆绕质心转动惯量)
- g = 9.8 m/s²
先用这组参数把模型调通,后面再根据自己的物理系统替换数值。注意I这个参数不能随意设太大,它影响系统固有频率,直接影响LQR中Q阵的选取范围。
2. 两条Simulink建模路线,我建议先从最笨的开始
Simulink里搭倒立摆模型有两条主流路线,我用“最笨的”来形容手动搭积分器的那条路线,不是贬义,恰恰相反,我强烈建议你先走这条路,因为它是理解模型的必经之路。
2.1 手动积分器搭非线性模型
这条路线完全基于第一节的非线性运动方程,用积分器、增益器、加法器把方程原样翻译成框图。具体做法是把ẍ和θ̈的表达式显式解出来:
ẍ = (F + mlθ̇²sinθ + ml(mlẍcosθ - mglsinθ)cosθ / (I + ml²)) / (M + m) θ̈ = (mglsinθ - mlẍcosθ) / (I + ml²)
注意ẍ和θ̈是耦合的,直接搭会形成代数环,Simulink会报警。解决的办法有两个:一是用“手工解耦”的方式,把两个方程联立求解,显式写出ẍ和θ̈关于状态变量的表达式;二是用一个MATLAB Function块,把非线性方程写进去,输入u和状态向量,输出ẍ和θ̈,这样最干净。
我在实操中更推荐MATLAB Function块 + 两个积分器链。因为当你想给模型加摩擦力、电机饱和、延迟这些非线性因素时,改函数内部逻辑比改一堆连线的模块要容易得多。具体结构是这样的:一个MATLAB Function接收F和当前状态x, ẋ, θ, θ̇,输出ẍ和θ̈;两个积分器分别对ẋ和θ̇积分得到位置和角度,反馈回函数输入。
2.2 用State-Space模块快速搭线性模型
如果你已经验证了非线性模型的正确性,并且暂时只关注线性控制器设计,可以直接用State-Space模块搭线性模型。在Simulink库中找到State-Space模块,把第一节算好的A、B矩阵填进去,输入接控制力F,输出按需要接一个Mux选择[x, ẋ, θ, θ̇]。
这个做法最大的优势是快、稳定、不出数值问题,方便快速验证控制器参数。缺点是看不到系统内部的非线性关系,如果你换了控制器之后仿真发散,你无法判断是控制器参数不合适,还是线性化模型本身在极限工况下失真。
我把两种做法都保留下来:平时调LQR参数用State-Space模型,验证真实控制效果或者要加非线性扰动时,切换到MATLAB Function搭的非线性模型。这样两套模型互相印证,比单用一个模型靠谱得多。
2.3 Simscape Multibody三维模型,适合看动画
如果你装了Simscape Multibody,也可以用三维机械模块直接搭建倒立摆。这个方法的好处是可视化强,能直观看到小车运动、摆杆倾倒的三维动画,对汇报演示特别有帮助。
具体步骤是:新建一个Simscape Multibody模型,从库中拖出World Frame、Mechanism Configuration,然后用地面的Prismatic Joint约束小车做水平移动,摆杆用Revolute Joint连接在小车上,摆杆本体用Brick或Cylinder表示。在关节上添加Joint Actuator输入力,Joint Sensor测量角度和位置。
这条路线要注意一个关键点:Simscape模型里每个机械连接都必须有明确的初始状态设置,尤其是Revolute Joint的初始角度,要在Joint模块的State Targets中设定一个小的初始偏移,否则Simulink默认从0度开始,刚好落在不稳定的平衡点上,仿真一步就卡住。Solver Configuration模块必须连接到机械系统上,通常放在模型里比较显眼的位置。
3. 控制器设计:PID为什么经常翻车,LQR怎么上手
模型搭好了,接下来就是重头戏:把摆稳定在竖直位置。很多教材和课程作业一开始都会让学生先试PID,但实测下来,纯角度PID的倒立摆仿真非常容易翻车,原因值得深入说一说。
3.1 为什么只靠角度PID难以稳定整个系统
倒立摆系统状态有4个:小车位置x、小车速度ẋ、摆杆角度θ、摆杆角速度θ̇。如果你只把θ作为PID反馈的误差信号,控制器相当于只“看见”了系统的一半状态。这个系统是四阶不稳定系统,单靠角度这一个通道的反馈,在参数上很难同时满足所有极点的配置需求。
最直观的表现是:角度PID调得太软时,摆杆慢慢倒下来;调得太硬时,小车会疯狂加速往一个方向冲,直到摆杆因为惯性甩过竖直位置,然后整个系统剧烈震荡发散。这是因为让摆杆稳定在竖直位置需要小车持续“追”着摆杆跑,如果速度状态没有任何反馈,控制动作是盲目的。
当然,你可以在角度PID的基础上把位置和速度也拉进反馈回路,形成多环PID,这在工程上确实有应用,但参数整定的复杂度远高于直接做一个全状态反馈。所以我的建议是:在仿真学习阶段就直接用LQR,理论清晰,参数设置也有章可循。
3.2 LQR调参思路与代码实现
LQR的全称是Linear Quadratic Regulator,核心思想是设计一个状态反馈控制律u = -KX,使得二次型性能指标J = ∫(XᵀQX + uᵀRu)dt最小。其中Q矩阵惩罚状态偏差,R矩阵惩罚控制能量,Q和R都是你的设计自由度。
在实际调参中,Q矩阵通常设为对角阵,四个对角元素分别对应位置、速度、角度、角速度的权重。我常用的初值是Q = diag([100, 10, 1000, 100]),R = 1。角度权重远大于位置权重,因为首要目标是立住摆杆,位置精度是次要的。如果仿真中发现控制力太大,就把R适当增大,比如从1改成5或10,代价是收敛速度变慢。
MATLAB命令极其简单:
% 定义系统参数 M = 0.5; m = 0.2; l = 0.3; I = 0.006; g = 9.8; den = M*(I + m*l^2) + M*m*l^2; % 状态空间矩阵 A = [0 1 0 0; 0 0 -(m^2*g*l^2)/den 0; 0 0 0 1; 0 0 m*g*l*(M+m)/den 0]; B = [0; (I + m*l^2)/den; 0; m*l/den]; % LQR求解 Q = diag([100, 10, 1000, 100]); R = 1; [K, S, e] = lqr(A, B, Q, R);运行之后看K矩阵和特征值e,e的实部必须全部为负,系统才稳定。如果某些特征值的实部接近0,说明系统接近稳定边界,需要调整Q或R。K矩阵里的四个元素分别对应四个状态的反馈增益,你可以很直观地看到角度状态对应的增益通常是所有通道里最大的,因为它是最需要“用力”拉回来的量。
3.3 在Simulink里接好状态反馈
把K矩阵代入Simulink模型有几种方式,我推荐最简单的一种:用一个Gain模块,参数设为K,输入接四个状态量的组合向量。
具体来说,把模型输出的x、ẋ、θ、θ̇用Mux合并成一个四维向量,乘上K矩阵得到一个标量控制信号u,经过负反馈取反,接到模型控制输入端。如果用的是State-Space模块,可以直接把这个u加到输入端。
要注意状态顺序必须和LQR设计时完全一致。我在调试中犯过一个低级错误:MATLAB里定义的状态顺序是[x, ẋ, θ, θ̇],但在Simulink里Mux时把顺序接成了[x, θ, ẋ, θ̇],结果K矩阵和实际状态完全错位,仿真结果诡异到无法解释。排查了很久才发现是Mux线序的问题。
如果使用了MATLAB Function的非线性模型,你可以在函数内部直接调LQR算好的K矩阵,把u = -K*X在函数内部计算,这样模块连线少很多,也比较方便后面把控制器部署成S-Function。
4. 仿真调试图鉴:实测中常见的坑与对策
最后这部分是干货中的干货。我从第一次搭倒立摆仿真到现在,把踩过的坑做个系统性的复盘,按出现频率排序,希望你能直接避开。
4.1 仿真发散第一反应不是调控制器,而是查步长
模型和控制器看起来都对,但仿真一跑不到1秒就发散,这是最常见的情况。很多人第一反应是控制器参数不对,开始疯狂调Q和R,其实问题的根源往往是求解器的步长设置不合理。
倒立摆系统的动态响应比较快,尤其是在摆杆接近竖直位置的邻域,角速度可能达到几十弧度每秒。如果使用固定步长求解器并且步长过大(比如0.01秒),数值积分会严重失真,系统快速发散的误差会被逐步放大。我的经验是:求解器选择variable-step,算法用ode45,最大步长(Max step size)设置为1e-3,必要时再启用Zero-crossing detection。改完步长之后,很多看似“控制器不稳定”的问题会自动消失。
另外顺便说一句,如果模型里用了Simscape Multibody,Simulink通常会自动加一个Solver Configuration模块并配置求解器,但你还是手动检查一下最大步长,机械系统默认配置在快速运动中并不总是合适。
4.2 初始角度给小一点,别相信完美平衡点
线性化模型是基于θ=0附近的小角度假设推导的,所以LQR控制器只在θ的“附近”是可靠的。如果你把初始角度设成30度(约0.52rad),那已经远远超出线性化的适用范围,控制器大概率会失效。
我实测下来,初始角度在0.05到0.2rad之间,LQR的控制效果都很好。超过0.3rad之后,控制器的稳定时间显著加长,甚至会出现先倒下一段再拉回来的现象。如果是非线性模型,超过0.5rad后控制器基本就走远了。
这里要强调一个容易忽略的细节:初始角度不要设成0。如果摆杆精确初始化为竖直向上,Simulink会认为系统处于平衡点,在没有初始速度的情况下,数值积分过程中控制器不会产生任何值得观察的响应,仿真结果看起来就像“什么都做不了”。故意给一个微小扰动,比如0.1rad,才能看到控制器的纠偏过程。
4.3 加入执行器饱和,仿真相差很大
纯线性模型里,LQR给出的控制力F可能非常大,尤其是初始偏差较大时,推力能瞬间飙到几百牛顿。但实际物理系统的执行器(比如直流电机或直线电机)是有推力上限的。
建议在Simulink模型中给控制输入端加一个Saturation限幅模块,把推力限制在合理范围,比如±10N或±20N。加了限幅之后,你会立刻发现控制器的“真实性能”:在大偏差下,推力饱和会导致响应变慢甚至失稳,这是线性控制器设计时很容易被忽视的实际约束。
如果你想继续深挖这个方向,可以研究一下抗饱和补偿器,或者用模型预测控制(MPC)把推力约束直接纳入优化目标。对于入门和进阶学习来说,先加一个Saturation模块观察影响就够了。
4.4 波形观察与数据导出的细节
Scope模块是Simulink里最常用的观测工具,但很多人用起来总觉得别扭。比如有人反馈“怎么在Scope里查看数组”,其实这是因为进入了错误的显示模式。简单说:如果信号是普通的时间序列,直接用默认的Scope视图即可;如果信号是多维数组或者你想同时对比多个信号,建议把数据通过To Workspace模块导出,格式选择timeseries或array,然后在MATLAB工作区里统一处理,绘图用plot(t, x(:, 3))之类的命令,比在Scope里调来调去方便得多。
我自己的习惯是给每个关键状态做好输出标记,位置、速度、角度、角速度都分开导出,后面做频谱分析或者参数扫描时,数据拿出来就能用,不用反复重新跑仿真。
还有一个小技巧:在Scope界面中,把采样间隔或数据点数目设置得足够多,否则你看到的是经过程序抽稀后的波形,可能漏掉仿真初期那几个最关键的振荡峰值。
从物理方程推导、非线性模型搭建,到LQR控制器设计与调试,再到各种限幅和数值求解细节,倒立摆这个看似“入门级”的仿真项目,实际上能把控制理论学习中的许多盲区都给照出来。我自己的体会是:如果只是照着网上的代码跑通一次,收获非常有限;但如果你愿意每遇到一个发散就回头查模型、查求解器、查状态顺序,查一次你就加深一层对系统的理解。仿真过程中踩过的这些坑,很多在实物控制里也会以类似的方式再现——执行器饱和、初始状态不稳定、观测噪声、数值离散误差,哪个都躲不掉。所以值得多花点时间在模型验证上,模型可信,控制器才有意义。
本文还有配套的精品资源,点击获取