news 2026/10/15 7:39:32

状态空间表达式入门:从微分方程到仿真与控制器设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
状态空间表达式入门:从微分方程到仿真与控制器设计

简介:《现代控制理论》第1章“控制系统的状态空间表达式”教学讲义PDF,适合具备线性代数与自动控制原理基础的自动化、电气工程等专业本科生,也适合希望从传递函数转入状态空间方法的初学者。内容从状态、状态变量、状态向量的定义讲起,说明状态空间表达式如何用矩阵微分方程描述系统动态,并给出标准形式(ẋ=Ax+Bu,y=Cx+Du);通过R-L-C电路实例,完整演示由电路微分方程建立状态空间模型的步骤,同时附绪论部分,简要梳理现代控制理论发展脉络及其与经典控制理论的差异。读者可搭配张嗣瀛《现代控制理论》或天津大学刘豹《现代控制理论》教材使用,也适合结合B站、中国大学MOOC的对应课程视频复习。资源为单个PDF文档,共2.79MB,已有199人学习下载,既便于课堂跟讲,也适合考前快速回顾状态空间建模的核心步骤。

1. 状态空间表达式到底是什么:从微分方程到系统描述的第一次升级

很多同学刚学完传递函数,以为控制理论就是拉普拉斯变换和伯德图。直到第一次看到状态空间表达式——满屏的矩阵、向量,形如 x' = Ax + Bu, y = Cx + Du,第一反应往往是“这比传递函数复杂太多了”。但真正上手做仿真、设计控制器之后才会明白,传递函数只描述外部输入输出关系,而状态空间表达式把系统的内部状态也放进了模型里。它天然适合多输入多输出(MIMO)系统、非线性时变系统,也是能控性、能观性、极点配置、最优控制这些现代控制理论概念的起点。这篇文章从零开始拆解状态空间表达式的建立、转换、仿真与避坑,让“第1章”不再只是背公式,而是能直接动手跑通的工具。

2. 从物理模型写出状态空间表达式:标准形式与建立步骤

2.1 状态变量的选取:不是随便选,要看系统阶次

状态空间表达式的核心是状态变量。状态变量的个数必须等于系统的阶次,也就是系统中独立储能元件的个数。这个规则看起来简单,但很多人第一次建模就栽在“多选了一个不独立的变量”上。

常见的错误是看到电容、电感就各取一个变量,结果两个变量之间存在代数约束,导致状态矩阵A奇异——仿真时一算就报错。正确的做法是:先列微分方程,再看方程最高阶导数的次数;或者直接数独立储能元件,哪些元件的电压/电流不能由其他变量线性表示。

在机械系统中,通常选位移和速度作为状态变量;在电路中,通常选电容电压和电感电流。选完之后,还有一个判断标准:如果把所有状态变量设为已知,系统在任意时刻的输出和未来行为是否已经完全确定。如果是,说明这组变量是完备的。

2.2 一个RLC电路的完整建模过程

以一个RLC串联电路为例:输入是电压源 u(t),输出是电容两端电压 y(t)。电阻 R、电感 L、电容 C 都是定常参数。

第一步,列回路方程:

u(t) = L·di/dt + R·i + y(t)

其中 i = C·dy/dt,所以:

u(t) = L·C·d²y/dt² + R·C·dy/dt + y

这是一个二阶系统,所以我们需要两个状态变量。最自然的选择是电容电压和电感电流,但在输出本身就是电容电压的情况下,选电容电压和电容电压的导数会更直接。为了演示标准形式,这里选择:

x1 = y(t)(电容电压) x2 = dy/dt(电容电压一阶导数,与电流成正比)

此时有:

dx1/dt = x2 dx2/dt = (1/LC)·u(t) - (R/L)·x2 - (1/LC)·x1

整理成矩阵形式:

| dx1/dt | | 0 1 | | x1 | | 0 | | dx2/dt | = | -1/LC -R/L | * | x2 | + | 1/LC | * u

输出方程为:

y = [1 0] * | x1 | | x2 |

写成标准形式就是:

A = [[0, 1], [-1/LC, -R/L]] B = [[0], [1/LC]] C = [[1, 0]] D = [[0]]

这里参数含义很明确:A 矩阵描述系统内部状态之间的耦合关系,B 矩阵描述输入如何进入状态,C 矩阵描述如何从状态得到输出,D 矩阵描述输入直接到输出的前馈通路。绝大多数物理系统 D 是零矩阵,因为输入很少能瞬间跳过储能元件直接出现在输出端。

2.3 建立状态方程的一般步骤与参数含义

把上面的过程抽象成一套可复用的步骤,之后遇到任何物理系统都能按这个流程走:

  1. 确定系统输入和输出,输入不一定只有一个,输出也可以有多个。
  2. 列写系统的微分方程组,机械系统用牛顿第二定律,电路系统用基尔霍夫定律。
  3. 确定系统阶次,选择状态变量。优先选择物理意义明确的量,比如位移、速度、电容电压、电感电流。
  4. 用状态变量的一阶导数表达原始微分方程,消去中间变量,得到 dx/dt = f(x, u)。
  5. 如果 f 是 x 和 u 的线性组合,就整理成矩阵形式,得到 A、B、C、D。

需要注意的是,状态变量的选择不唯一,可以选 x1 = 电容电压,x2 = 电感电流,也可以通过线性变换得到另一组状态变量。不同选择下 A、B、C、D 完全不同,但系统的输入输出特性不变。判断你是否写对的标准是:用这组矩阵算出来的传递函数 C(sI-A)⁻¹B + D,必须和原始系统的传递函数一模一样。

3. 从传递函数到状态空间:能控标准型与能观标准型怎么选

3.1 直接分解法:分子分母多项式怎么拆

实际工程里,很多时候我们无法从物理机理出发建模,只能通过实验测出传递函数。比如一个系统的传递函数是:

G(s) = (s+2) / (s² + 3s + 2)

那么怎么变成状态空间表达式?最经典的方法是直接分解法,也叫可控标准型实现。把传递函数写成:

G(s) = Y(s)/U(s) = (b₁s + b₀) / (s² + a₁s + a₀)

引入中间变量 Z(s),令 Y(s) = (b₁s + b₀)Z(s),U(s) = (s² + a₁s + a₀)Z(s)。

对 U(s) 那个式子取拉普拉斯逆变换(零初值),得到:

z'' + a₁z' + a₀z = u

选状态变量 x1 = z,x2 = z',就有:

|x1'| | 0 1 | |x1| |0| |x2'| = | -a₀ -a₁ | |x2| + |1| u

输出 y = b₁z' + b₀z = b₁x2 + b₀x1,所以:

C = [b₀, b₁]

D = 0

这就是能控标准型。它的特点是 A 矩阵最后一列是 -a₀, -a₁,B 矩阵最后一行是 1,这样的形式能直观看出输入对最后一个状态的影响,方便后续做极点配置。

同样还有一种能观标准型,A 矩阵变成转置关系,C 矩阵变成 [1, 0] 这种形式,主要用于状态观测器设计。实际选择取决于你要干什么:做状态反馈控制,优先能控标准型;做状态估计,优先能观标准型。

3.2 用 Python 做转换:tf2ss 与 ss2tf 的输入输出约定

手动拆多项式只适用于低阶系统,阶次一高就容易出错。常见做法是用 scipy.signal 里的 tf2ss 和 ss2tf 快速转换。代码如下:

import numpy as np import scipy.signal as sig # 传递函数 G(s) = (s + 2) / (s^2 + 3s + 2) num = [1, 2] # 分子系数:s + 2 den = [1, 3, 2] # 分母系数:s^2 + 3s + 2 # 传递函数 -> 状态空间(默认返回能控标准型) A, B, C, D = sig.tf2ss(num, den) print("A =", A) print("B =", B) print("C =", C) print("D =", D) # 状态空间 -> 传递函数,验证是否一致 num_back, den_back = sig.ss2tf(A, B, C, D) print("还原后的分子:", num_back) print("还原后的分母:", den_back)

逻辑说明:tf2ss 接收分子分母系数数组,系数按降幂排列,返回的 A 矩阵默认是能控标准型。ss2tf 是逆过程,返回的分子分母可能会因为内部变换出现与原传递函数相差一个常数倍的情况,比如分子是 [0.5, 1] 而分母是 [0.5, 1.5, 1],此时只要约掉比例因子即可。

参数说明:num 和 den 的长度不同也没关系,tf2ss 会自动补零。但要注意,如果 num 和 den 有公因子,tf2ss 返回的状态空间可能不是最小实现,也就是说状态维数比系统真实阶次高,后面我们会在避坑章节专门讲这个问题。

3.3 能控标准型和能观标准型的适用边界

直接分解法得到的能控标准型,B 矩阵是 [0,...,1]ᵀ,这保证了系统一定是能控的。但能观性不一定成立,如果传递函数有零极点对消,对消后的极点就会在能控标准型中变成不可观的模态。

反过来,能观标准型保证能观,但不保证能控。设计反馈控制器时,如果系统本身有不可控模态,极点配置就会失败——你配置的极点只对可控部分有效,不可控部分的极点还留在原地。判断到底用哪种标准型,不能只看习惯,要看后续环节:状态反馈用能控标准型,状态观测器用能观标准型。如果要同时做反馈和观测,就得检查系统是否同时能控且能观,也就是你没有零极点对消。

4. 状态空间表达式的求解与仿真:从解析解到数值仿真

4.1 状态转移矩阵:为什么零输入响应是 e^{At}

零输入情况下,状态方程变成 x' = A x,这是一个一阶向量微分方程。它的解析解是:

x(t) = e^{A(t-t0)} x(t0)

这里的 e^{At} 不是标量指数,而是矩阵指数,定义是无穷级数:

e^{At} = I + At + (At)²/2! + ...

矩阵指数描述了系统在没有外部输入时,状态从初始值自然演化的规律,所以叫状态转移矩阵。在 MATLAB 或 Python 里计算 e^{At} 不能直接对每个元素求 exp,要用专门函数,比如 scipy.linalg.expm,它内部采用Padé近似,数值稳定性远好于直接截断级数。

对于有输入的情况,完整解是:

x(t) = e^{A(t-t0)} x(t0) + ∫_{t0}^{t} e^{A(t-τ)} B u(τ) dτ

这个式子就是卷积的连续形式。如果 u 是常数,积分可以直接算出来,得到熟悉的零阶保持离散化公式。

4.2 用 Python 模拟状态方程:步进响应与初始状态响应

实际工程中很少手推解析解,直接用数值积分或者离散化。下面这段 Python 代码演示了如何模拟一个二阶系统的阶跃响应和初始状态响应:

import numpy as np import matplotlib.pyplot as plt from scipy.signal import lti, step, lsim # 系统:RLC电路,R=1欧姆,L=1H,C=1F A = np.array([[0, 1], [-1/(1*1), -1/1]]) B = np.array([[0], [1/(1*1)]]) C = np.array([[1, 0]]) D = np.array([[0]]) system = lti(A, B, C, D) # 时间向量 t = np.linspace(0, 10, 1000) # 阶跃响应 t_step, y_step = step(system, T=t) # 零输入响应:初始状态 x0 = [1, 0] u_zero = np.zeros_like(t) t_sim, y_zero, x_zero = lsim(system, U=u_zero, T=t, X0=[1, 0]) plt.figure(figsize=(10, 4)) plt.subplot(1, 2, 1) plt.plot(t_step, y_step) plt.title("Step Response") plt.xlabel("t"); plt.ylabel("y") plt.subplot(1, 2, 2) plt.plot(t_sim, y_zero) plt.title("Zero-Input Response (x0=[1,0])") plt.xlabel("t"); plt.ylabel("y") plt.tight_layout() plt.show()

逻辑说明:lti 是 scipy.signal 中的线性时不变系统对象,直接用 A、B、C、D 构建。step 函数计算单位阶跃响应,lsim 函数可以任意指定输入序列和初始状态 X0。零输入响应里把输入置零,只保留初始状态的影响,这在验证状态转移矩阵时非常有用。

参数说明:T 是时间采样点,越密曲线越平滑但计算量增大。X0 是初始状态向量,必须和 A 的维数一致,否则 lsim 会报维度错误。如果系统有直接馈通 D≠0,阶跃响应在 t=0 处会有一个跳变,这是正常现象,不用怀疑代码错了。

4.3 连续系统离散化:零阶保持到底怎么用

数字控制器里需要把连续状态空间模型离散化。最常见的零阶保持(ZOH)离散化公式是:

x[k+1] = A_d x[k] + B_d u[k] y[k] = C x[k] + D u[k]

其中 A_d = e^{ATs},B_d = ∫_0^{Ts} e^{Aτ} B dτ,Ts 是采样周期。

在 Python 中, scipy.signal.cont2discrete 可以一键完成:

import scipy.signal as sig Ts = 0.1 # 采样周期 0.1 秒 Ad, Bd, Cd, Dd, _ = sig.cont2discrete((A, B, C, D), Ts, method='zoh') print("Ad =", Ad) print("Bd =", Bd)

method='zoh' 表示零阶保持。还有 'foh'(一阶保持)和 'bilinear'(双线性变换)。选 ZOH 是默认做法,因为它模拟了真实 DAC 在每个采样周期内保持输出恒定的行为。如果采样时间选择过大,离散化模型的动态会失真,一般建议 Ts 小于系统最小时间常数的 1/5。

5. 状态空间建模避坑指南:5 个让你翻车的细节

5.1 状态变量数量不等于系统阶次

现象:建模时随手多选了一个电压或电流作为状态变量,算出来 A 矩阵行列式为零,或者仿真时状态变量异常发散。

原因:选择了不独立的储能元件变量。比如一个电容和一个电感并联,电容电压和电感电压受同一个节点约束,实际上只有一个独立的动态变量,你却选了俩。

解决:回归微分方程,看特征多项式的最高阶次;或者对候选状态变量做线性相关性检查。如果 A 矩阵奇异,基本可以断定状态变量之间存在代数约束。

5.2 传递函数零极点对消导致的最小实现问题

现象:用 tf2ss 转换一个传递函数 (s+1)/(s²+2s+1),得到的状态空间是 2 阶,但分子分母约掉 (s+1) 后其实是一阶系统。仿真时发现有一个状态既不不可观也不可控,但把它删掉后系统输入输出特性不变。

原因:tf2ss 不做最小实现约减,只是机械地按分母阶次分配状态。

解决:先用 scipy.signal.tf2zpk 求出零点和极点,检查是否有相同值,如果有就约掉;或者转换后用 minimal realization 函数(比如 scipy.signal.minreal)做最小实现。在做状态反馈之前务必做这一步,否则你会尝试配置一个根本不可控的极点。

5.3 离散化方法选错,高频段仿真结果对不上

现象:用双线性变换离散化同一个系统,仿真阶跃响应和 ZOH 的结果在低频一致,但在高频段相差很大。

原因:双线性变换把整个 s 平面映射到 z 平面,频响会扭曲;ZOH 则更接近实际数字控制的行为。

解决:仿真数字控制器时优先选 ZOH;如果要分析频率响应,再用双线性并配合预畸变(pre-warp)。采样周期越大,两者差异越明显,所以要先评估系统带宽,再定 Ts。

5.4 初始状态设置错误导致响应发散

现象:lsim 时 X0 给了 [1, 0, 0](3 个数),但 A 是 2×2,直接报错;或者 X0 给了 [1, 0],系统明明稳定,响应却指数增长。

原因:维度不匹配是显性错误,好排查。另一种更隐蔽的是状态变量物理意义与初值不对应,比如你把电容电压初值给了电感电流,虽然数值上能跑,但已经不是你要模拟的物理场景。

解决:写代码前先明确每个状态变量对应的物理量。给 X0 时逐元素注释,例如 x0 = np.array([1.0, 0.0]) # [电容电压, 电感电流]。如果在调试时响应异常,先把输入置零,看零输入响应是否符合物理直觉。

5.5 单位不一致,仿真结果差好几个数量级

现象:电路仿真用 mV 和 mA,机械仿真用 mm 和 kg,得到的状态空间数值巨大,e^{At} 计算溢出或者收敛极慢。

原因:状态空间表达式对单位极其敏感,A 矩阵的元素带有 1/s 量纲,B 和 C 的元素带有跨物理量纲的增益。如果单位没统一,A 的特征值可能比数值积分步长高出几个数量级。

解决:建模时统一用国际单位制(SI)。如果必须用工程单位,先做无量纲化或者标幺化,把 A 矩阵的范数尽量控制在 1 附近。高档系统仿真工具里会有单位检查,但在自己的脚本里就得靠经验了。

6. 从表达式到控制器设计:一个让状态空间活过来的最小例子

状态空间表达式最大的价值不是用来画波形,而是用来设计状态反馈控制器。这里给出一个最经典的极点配置例子,把第 4 章那个 RLC 系统通过状态反馈把极点配置到指定位置。

假设我们想让系统闭环极点都在 s = -2 和 s = -3,也就是希望系统的响应更快且无超调。状态反馈控制律是 u = -Kx,其中 K = [k1, k2]。把 u 代入状态方程:

x' = (A - BK)x

所以闭环系统矩阵是 A - BK,其特征值就是闭环极点。问题变成:找 K 使得 det(sI - (A-BK)) = (s+2)(s+3) = s² + 5s + 6。

Python 里可以直接用 place_poles:

import numpy as np from scipy.signal import place_poles A = np.array([[0, 1], [-1, -1]]) # L=1, C=1, R=1 B = np.array([[0], [1]]) C = np.array([[1, 0]]) desired_poles = [-2, -3] result = place_poles(A, B, desired_poles) K = result.gain_matrix print("状态反馈增益 K =", K) # 验证闭环极点 closed_A = A - B @ K eig_vals = np.linalg.eigvals(closed_A) print("闭环极点:", eig_vals)

运行后你会看到 K 的值。这个 K 就是让系统具备你想要动态特性的“密码”。把 K 代入仿真,你会发现输出响应比开环快很多,而且没有振荡。

极小范例到这里,你已经把状态空间表达式的建立、转换、仿真、离散化、反馈设计全链路打通了。这个方向如果要继续深入,下一步可以尝试加状态观测器,或者用 LQR 代替极点配置来求解最优 K。我自己的习惯是:每学一个新系统模型,第一件事就是先写出 A、B、C、D,然后跑一遍零输入响应和阶跃响应,确认物理行为合理,再开始设计控制律。这是避开所有玄学问题的最笨也最可靠的办法。希望这些踩坑经验能帮你少走一些弯路,让状态空间真正变成一个可以拿来干活的分析工具。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 7:39:17

论文查重与AIGC检测双重暴击?paperzz双引擎降重降AI痕迹实操指南

毕业季一到,实验室通宵的灯就没熄过。除了实验数据,最让人头疼的是论文查重率突然拉高,紧接着又被AIGC检测标成“疑似AI生成”。我去年帮几位学弟学妹改稿,遇到的就是这种“双重暴击”。当时自己动手写了一个内部辅助工具&#xf…

作者头像 李华
网站建设 2026/10/15 7:38:57

Java学生综合评测系统:数据库设计、评分算法与权限控制实战

每年的毕业设计阶段,Java方向的学生抛给我的题目里,“学生综合评测系统”的出现频率一直很高。这个选题看起来不惊艳,但认真做起来其实非常完整:有表单提交、多角色评分、权限控制、加权汇总,还有导出报表,…

作者头像 李华
网站建设 2026/10/15 7:38:23

深入浅出:拆解实现skill

🌟 深入浅出:揭开 AI Agent 中 Skill 的神秘面纱 随着大语言模型(LLM)的火热,AI Agent(智能体)的概念也越来越普及,几乎每家公司都在尝试打造自己的“超级助理”。 但在实际开发和使…

作者头像 李华
网站建设 2026/10/15 7:38:01

《小王子》第十六章的文学分析与哲学启示

1. 章节背景与文学价值解析《小王子》第十六章作为圣埃克苏佩里这部经典作品的关键转折点,描绘了主人公来到地球后的首个深刻观察。这一章通过地理学家角色引出了全书核心命题——成年人世界与孩童视角的本质冲突。开篇用"第七颗行星是地球"的平淡陈述&am…

作者头像 李华
网站建设 2026/10/15 7:37:56

ConcurrentHashMap 1.7与1.8源码对比:从Segment到CAS+synchronized的并发演进

做Java开发的朋友,多少都被ConcurrentHashMap“教育”过。面试要问,源码分析要讲,线上排查高并发问题也绕不开它。而只要一聊到源码,1.7和1.8这两个版本的区别就是永远躲不开的坎:一个是Segment分段锁,一个…

作者头像 李华