简介:这份资源是B站博主Dr.CAN【自动控制原理】专题的完整学习笔记,共13期,面向控制理论零基础或基础不牢的本科生、研究生,尤其适合准备深入卡尔曼滤波、MPC与最优控制方向的学习者。笔记以QQ截屏配合GoodNotes整理,覆盖开环与闭环系统、反馈控制、传递函数、稳定性分析、根轨迹绘制、终值定理与稳态误差、比例积分控制器等核心知识点,并配有平衡车、单摆、减肥代谢等直观类比帮助理解极点分布与系统稳定性。资源包为1个PDF文件,大小约12.27MB,内容按专题顺序编排,便于对照视频逐期复习。目前已有847人学习下载,适合想快速建立自动控制知识框架、为后续进阶课程打基础的同学参考使用。
1. 自动控制原理的笔记为什么值得当成工程手册来啃
很多人第一次翻自动控制原理,都会被拉普拉斯变换、根轨迹、奈奎斯特曲线这一套组合拳打懵,觉得它更像数学课而不是工程课。但真正做过电机调速、温控、无人机姿态或者电源环路补偿的人会告诉你:这门课是少数几门能直接决定你产品稳不稳、响不响、会不会自激的硬功夫。Dr.CAN 这套 13 期全的课程笔记,价值不在于把公式抄一遍,而在于它把「建模—分析—校正—仿真」这条链路完整串了起来,让你在 MATLAB 里跑出一条曲线时,知道它对应现实里哪个物理量在作妖。这篇笔记面向的是想把这门课真正用起来的人:刚学完理论想找落地路径的学生、转行做控制相关开发的工程师、以及需要补系统建模能力的嵌入式从业者。下面我按自己复现这套笔记的路径,把选型理由、参数设置和踩过的坑讲清楚。
2. 从微分方程到传递函数:建模这一步决定了后面所有分析的上限
2.1 为什么先建模再谈控制,而不是直接上 PID
我见过太多人一上来就调 PID,结果波形振荡就加微分,响应慢就加比例,最后系统稍微换个工况就崩。根子在于没有先拿到被控对象的传递函数。自动控制原理里最核心的一步,是把物理系统写成微分方程,再做拉普拉斯变换得到传递函数。这个过程看起来繁琐,但它决定了你后面所有频域分析、稳定性判据、校正设计有没有意义。Dr.CAN 笔记里反复强调的一点是:建模误差会直接放大成控制误差,模型错了,控制器再花哨也是白搭。
常见做法是先用机理建模拿到大致结构,再用实验辨识修正参数。比如一个直流电机,机理上可以写成电枢回路电压方程加机械转动方程,联立消去中间变量后得到角速度对电压的传递函数。但实际电机有摩擦、有饱和、有死区,纯机理模型往往对不上实测曲线,这时候就需要用阶跃响应去反推时间常数和增益。
2.2 用 Python 做符号推导和传递函数构建
笔记里用的是 MATLAB,但很多人手头没有授权,我一般会用 Python 的 sympy 和 control 库来复现。下面这段代码把 RLC 电路的传递函数推出来,并转成 control 库能用的形式。
import sympy as sp import control as ctrl # 定义符号 s, R, L, C = sp.symbols('s R L C') Vin, Vout = sp.symbols('Vin Vout') # RLC 串联电路:Vin 为输入,Vout 为电容两端电压 # 回路电流 I = Vin / (R + L*s + 1/(C*s)) # Vout = I * 1/(C*s) I = Vin / (R + L*s + 1/(C*s)) Vout_expr = sp.simplify(I * (1/(C*s))) # 得到传递函数 Vout/Vin H_sym = sp.simplify(Vout_expr / Vin) print("符号传递函数:", H_sym) # 代入具体参数:R=10Ω, L=1mH, C=100uF H_num = sp.simplify(H_sym.subs({R: 10, L: 1e-3, C: 100e-6})) print("数值传递函数:", H_num) # 提取分子分母系数,转成 control.TransferFunction num, den = sp.fraction(sp.simplify(H_num)) num_coeffs = [float(c) for c in sp.Poly(num, s).all_coeffs()] den_coeffs = [float(c) for c in sp.Poly(den, s).all_coeffs()] sys = ctrl.tf(num_coeffs, den_coeffs) print("control 传递函数:", sys)这段代码的逻辑是先用 sympy 做符号运算,保证推导过程可追溯,再代入具体数值得到数值传递函数,最后提取系数喂给 control 库。参数说明:R 是电阻,L 是电感,C 是电容,这三个值直接决定系统的自然频率和阻尼比。如果你改 C 的值,会看到谐振峰位置移动,这就是后面频域分析要盯的点。注意 sp.fraction 返回的是分子分母表达式,sp.Poly 的 all_coeffs 是从最高次到常数项排列,control.tf 要求的就是这个顺序。
2.3 建模阶段最容易忽略的初始条件和线性化范围
笔记里有一期专门讲小信号线性化,这个点很多人跳过,结果在实际系统里翻车。比如一个带死区的阀门,你在工作点附近线性化得到的传递函数,只在这个工作点附近有效。一旦工况偏离,模型就失效,控制器表现会突然变差。我一般会在建模文档里明确标注线性化工作点和适用范围,后面调参时如果发现响应异常,先回头检查是不是跑出了线性区。
另一个坑是初始条件。拉普拉斯变换默认零初始条件,但实际系统启动时往往不是零状态。如果你做的是启动过程控制,零初始条件的传递函数只能描述增量行为,绝对量要对齐。这个细节在笔记里没有展开,但做工程时必须心里有数。
3. 时域和频域分析:怎么从一条曲线读出系统会不会振荡
3.1 阶跃响应里藏着的四个关键指标
时域分析最直接的工具就是阶跃响应。笔记里把上升时间、峰值时间、超调量、调节时间这四个指标讲得很细,但我想补充的是它们之间的耦合关系。你不能单独追求某一个指标,比如把上升时间压得很短,超调量往往就会飙上去。我一般会先确定调节时间的硬指标,再在这个约束下找超调量和上升时间的平衡点。
用 control 库可以直接拿到这些指标。下面这段代码对一个二阶系统做阶跃响应,并自动提取指标。
import control as ctrl import numpy as np # 二阶系统:自然频率 10 rad/s,阻尼比 0.5 wn = 10 zeta = 0.5 sys = ctrl.tf([wn**2], [1, 2*zeta*wn, wn**2]) # 阶跃响应 t, y = ctrl.step_response(sys) # 提取指标 info = ctrl.step_info(sys) print("上升时间:", info['RiseTime']) print("峰值时间:", info['PeakTime']) print("超调量:", info['Overshoot']) print("调节时间(2%):", info['SettlingTime']) # 画图看形状 import matplotlib.pyplot as plt plt.plot(t, y) plt.axhline(1, color='r', linestyle='--') plt.xlabel('Time (s)') plt.ylabel('Amplitude') plt.title('Second-order Step Response') plt.grid(True) plt.show()逻辑说明:ctrl.step_info 会自动根据响应曲线计算指标,但它的调节时间默认用 2% 误差带,如果你需要 5% 误差带,得自己写判断逻辑。参数说明:wn 是自然频率,决定响应快慢;zeta 是阻尼比,决定超调大小。zeta 小于 1 是欠阻尼,会有超调;等于 1 是临界阻尼,无超调但响应偏慢;大于 1 是过阻尼,响应更慢。实际调参时,zeta 一般取 0.4 到 0.8 之间,兼顾速度和超调。
3.2 伯德图判稳:增益裕度和相位裕度到底看哪个
频域分析里,伯德图是最常用的工具。笔记里讲增益裕度和相位裕度时,很多人会问到底以哪个为准。我的经验是:两个都要看,但优先级取决于你的系统特性。如果系统相位下降很快,相位裕度更关键;如果系统增益变化剧烈,增益裕度更关键。一般工程上要求相位裕度大于 45 度,增益裕度大于 6 dB,这是底线。
用 control 库画伯德图并读取裕度:
import control as ctrl import matplotlib.pyplot as plt # 开环传递函数:G(s) = 100 / (s*(s+10)) sys = ctrl.tf([100], [1, 10, 0]) # 画伯德图 ctrl.bode_plot(sys, dB=True, Hz=False) plt.show() # 读取稳定裕度 gm, pm, wcg, wcp = ctrl.margin(sys) print(f"增益裕度:{20*np.log10(gm):.2f} dB (在 {wcg:.2f} rad/s)") print(f"相位裕度:{pm:.2f} 度 (在 {wcp:.2f} rad/s)")逻辑说明:ctrl.margin 返回增益裕度 gm、相位裕度 pm、相位穿越频率 wcg、增益穿越频率 wcp。注意 gm 是绝对值,转成 dB 要取 20*log10。参数说明:如果 pm 是负的,说明系统已经不稳定;如果 pm 很小比如小于 30 度,虽然理论稳定,但实际因为建模误差和扰动,很容易振荡。我一般会把 pm 调到 50 度以上再交付。
3.3 根轨迹法在参数扫描时的独特价值
根轨迹法适合看某个参数从 0 变到无穷时闭环极点的走向。笔记里有一期专门讲根轨迹绘制规则,但实际工程中我更常用它来快速判断增益 K 的可调范围。比如你设计了一个比例控制器,想知道 K 从多少开始系统会不稳定,根轨迹图一眼就能看出来。
用 control 库画根轨迹:
import control as ctrl import matplotlib.pyplot as plt # 开环传递函数:G(s) = 1 / (s*(s+2)*(s+5)) sys = ctrl.tf([1], [1, 7, 10, 0]) # 画根轨迹 ctrl.root_locus(sys) plt.show()逻辑说明:root_locus 会自动扫描增益 K,画出闭环极点轨迹。参数说明:轨迹与虚轴的交点对应的 K 就是临界增益。如果轨迹全部在左半平面,说明系统对任意 K 都稳定;如果有分支进入右半平面,就要限制 K 的上限。这个图在调参时比反复跑仿真快得多。
4. 校正设计:超前、滞后、超前滞后到底怎么选
4.1 三种校正网络的适用场景对比
笔记里把超前校正、滞后校正、超前滞后校正讲得很清楚,但实际选型时容易懵。我一般按下面的逻辑判断:如果系统响应慢但稳定性够,用超前校正提高带宽和响应速度;如果系统稳定性不够但响应速度可以接受,用滞后校正牺牲一点带宽换稳定性;如果既要快又要稳,用超前滞后校正,但设计复杂度最高。
下面这张表是我自己总结的选型参考:
| 校正类型 | 主要作用 | 代价 | 典型场景 |
|---|---|---|---|
| 超前校正 | 提高相位裕度,加快响应 | 高频增益抬高,噪声敏感 | 伺服系统、无人机姿态 |
| 滞后校正 | 提高低频增益,改善稳态误差 | 带宽降低,响应变慢 | 温控系统、液位控制 |
| 超前滞后 | 兼顾速度和精度 | 设计复杂,参数多 | 高性能调速、机器人关节 |
4.2 用 Python 设计一个超前校正器
下面这段代码设计一个超前校正器,把相位裕度从不足提到 50 度以上。
import control as ctrl import numpy as np # 原始开环传递函数 sys = ctrl.tf([10], [1, 3, 2, 0]) # 查看原始裕度 gm0, pm0, _, _ = ctrl.margin(sys) print(f"原始相位裕度:{pm0:.2f} 度") # 目标相位裕度 pm_target = 50 phi = pm_target - pm0 + 5 # 加 5 度余量 phi_rad = np.deg2rad(phi) # 超前校正器参数计算 alpha = (1 + np.sin(phi_rad)) / (1 - np.sin(phi_rad)) # 选择校正器零点频率,一般放在穿越频率附近 wc = 3 # 假设穿越频率约 3 rad/s T = 1 / (wc * np.sqrt(alpha)) # 超前校正器:Gc(s) = (alpha*T*s + 1) / (T*s + 1) Gc = ctrl.tf([alpha*T, 1], [T, 1]) print("校正器传递函数:", Gc) # 校正后系统 sys_comp = Gc * sys gm1, pm1, _, _ = ctrl.margin(sys_comp) print(f"校正后相位裕度:{pm1:.2f} 度") # 对比阶跃响应 t, y0 = ctrl.step_response(ctrl.feedback(sys, 1)) t, y1 = ctrl.step_response(ctrl.feedback(sys_comp, 1)) import matplotlib.pyplot as plt plt.plot(t, y0, label='Before') plt.plot(t, y1, label='After') plt.legend() plt.grid(True) plt.show()逻辑说明:先算原始相位裕度,再根据目标裕度算需要的相位超前量 phi,然后由 phi 算 alpha,再由穿越频率算时间常数 T。参数说明:alpha 越大,超前越多,但高频增益也越高,噪声放大越严重。T 决定校正器作用的频率范围。注意这里 wc 是假设的穿越频率,实际设计时要迭代几次,因为加校正器后穿越频率会移动。我一般会先估一个 wc,设计完再看新的穿越频率,如果偏差大就重来。
4.3 滞后校正的参数整定和常见误用
滞后校正的核心是把低频增益抬上去,同时不破坏高频段的相位裕度。常见误用是把滞后校正器的零点放得太靠近穿越频率,结果相位被拉低,系统反而更不稳定。我一般把滞后校正器的零点放在穿越频率的十分之一以下,极点再往左推一个 alpha 倍。
# 滞后校正器设计 beta = 10 # 低频增益提升倍数 wc = 3 # 穿越频率 T = 10 / wc # 零点频率取穿越频率的 1/10 # 滞后校正器:Gc(s) = (T*s + 1) / (beta*T*s + 1) Gc_lag = ctrl.tf([T, 1], [beta*T, 1]) print("滞后校正器:", Gc_lag)参数说明:beta 决定低频增益提升多少,T 决定作用频率范围。注意滞后校正会降低系统带宽,响应变慢,所以如果响应速度是硬指标,要慎重。
5. 避坑与排查:仿真能跑通但实物就振荡的五个原因
5.1 现象:仿真阶跃响应很漂亮,实物一上电就高频振荡
原因:仿真用的是理想模型,实物有开关噪声、采样延迟、传感器噪声。这些高频成分在仿真里不存在,但实物里会被控制器放大。解决:在仿真里加入噪声源和延迟环节,重新验证相位裕度。如果相位裕度在加入延迟后掉到 30 度以下,说明实际系统不够稳,需要重新设计校正器。
5.2 现象:调大比例增益后响应变快,但稳态误差反而变大
原因:比例增益太大导致系统进入非线性区,比如运放饱和、PWM 限幅。这时候反馈回路实际上开环了,稳态误差自然失控。解决:检查执行机构的最大输出能力,在控制器输出后加限幅,并重新调参。我一般会在仿真里加饱和模块,提前看到这个现象。
5.3 现象:根轨迹显示稳定,但实物在某些工况下失稳
原因:根轨迹用的是线性模型,实物在工作点变化后模型参数漂移。比如电机电感随电流变化,电阻随温度变化。解决:做参数敏感性分析,把关键参数上下浮动 20% 再跑一遍稳定性判据。如果裕度不够,要么换更鲁棒的控制器,要么加自适应机制。
5.4 现象:伯德图相位裕度够,但阶跃响应超调很大
原因:相位裕度和超调量的对应关系是在二阶系统假设下成立的,实际系统可能是高阶的,有谐振峰。解决:看闭环频率响应,如果谐振峰大于 3 dB,超调就会明显。这时候要加陷波器或者降低带宽。
5.5 现象:数字控制器里离散化后性能变差
原因:连续域设计的控制器离散化时,如果采样频率不够高,相位滞后会吃掉裕度。解决:采样频率至少取带宽的 20 倍以上,离散化方法优先用 Tustin 或零极点匹配。下面这段代码对比连续和离散控制器的阶跃响应:
import control as ctrl sys = ctrl.tf([100], [1, 10, 0]) Gc = ctrl.tf([1, 1], [0.1, 1]) # 假设的连续控制器 # 离散化,采样周期 0.01s Ts = 0.01 Gc_d = ctrl.c2d(Gc, Ts, method='tustin') print("离散控制器:", Gc_d) # 闭环对比 sys_c = ctrl.feedback(Gc * sys, 1) sys_d = ctrl.feedback(Gc_d * ctrl.c2d(sys, Ts, method='zoh'), 1) t, y_c = ctrl.step_response(sys_c) t, y_d = ctrl.step_response(sys_d, T_num=1000) import matplotlib.pyplot as plt plt.plot(t, y_c, label='Continuous') plt.plot(t, y_d, label='Discrete') plt.legend() plt.grid(True) plt.show()参数说明:Ts 是采样周期,method 是离散化方法。注意离散化后响应会有轻微差异,如果差异过大,要么减小 Ts,要么换离散化方法。
6. 把笔记变成自己的工具:一套可复用的仿真验证流程
学完这 13 期笔记,如果只是看懂,过两周就忘了。我的习惯是每学一个知识点,就写一个可复用的仿真脚本,存到自己的工具箱里。下面这套流程是我自己用的,从建模到校正到验证,每一步都有对应的脚本模板。
第一步,建模脚本。输入物理参数,输出传递函数。我一般会把符号推导和数值代入分开写,方便后面改参数。第二步,分析脚本。输入传递函数,输出阶跃响应指标、伯德图裕度、根轨迹图。第三步,校正脚本。输入原始系统和目标裕度,输出校正器参数和校正后性能。第四步,验证脚本。加入噪声、延迟、饱和,重新跑一遍,看性能退化多少。
这套流程跑下来,一个控制系统的设计周期能从几天缩到几个小时。更重要的是,每次踩坑后,我会把坑和解决方案写进脚本注释里,下次遇到类似问题直接查。比如有一次我忘了加限幅,仿真里响应完美,实物一上电就饱和振荡,后来我在验证脚本里强制加了饱和模块,再也没犯过这个错。
还有一个技巧是参数扫描。不要只调一组参数,用循环扫一遍关键参数,看性能指标怎么变。下面这段代码扫描阻尼比和自然频率,画出超调量和调节时间的热力图:
import numpy as np import control as ctrl import matplotlib.pyplot as plt zeta_range = np.linspace(0.2, 1.0, 20) wn_range = np.linspace(1, 20, 20) overshoot = np.zeros((len(zeta_range), len(wn_range))) settling = np.zeros_like(overshoot) for i, zeta in enumerate(zeta_range): for j, wn in enumerate(wn_range): sys = ctrl.tf([wn**2], [1, 2*zeta*wn, wn**2]) info = ctrl.step_info(sys) overshoot[i, j] = info['Overshoot'] settling[i, j] = info['SettlingTime'] plt.figure() plt.contourf(wn_range, zeta_range, overshoot, levels=20) plt.colorbar(label='Overshoot (%)') plt.xlabel('Natural Frequency (rad/s)') plt.ylabel('Damping Ratio') plt.title('Overshoot Map') plt.show()逻辑说明:双重循环遍历阻尼比和自然频率,每个组合算一次阶跃响应指标,最后画成热力图。参数说明:zeta_range 和 wn_range 的范围根据你的系统调整。这张图能帮你快速找到满足超调要求的参数区域,不用一个个试。
最后说一个我自己的教训:不要迷信仿真。仿真只能验证逻辑,不能替代实物调试。我一般会在仿真通过后,先用小功率、低速度跑实物,确认基本稳定后再逐步加到额定工况。每次加码前,先想好如果振荡了怎么紧急停机。这个习惯让我避免了好几次炸机。希望帮到你。
本文还有配套的精品资源,点击获取