系统建模——从物理方程到数学模型
上篇聊了反馈控制的基本原理,讲了闭环为什么比开环好。但在你设计任何控制器之前,有一件事必须先做——给你的被控对象建一个数学模型。
说白了,你得先搞清楚"你要控制的东西到底是怎么动的",才能设计控制器让它按你的意愿动。
系统建模这件事,很多教科书讲得特别枯燥——一上来就是拉格朗日方程、哈密顿原理。今天我们换个思路,从最直观的物理系统出发,一步步把数学模型推出来,让你理解建模到底在干嘛,面试的时候能讲清楚。
为什么要建模
你可能觉得建模是多此一举——我直接调PID不就行了?
讲真,不调模型直接调PID确实能跑,但效果往往不好。为什么?因为你不知道你的系统响应有多快、惯量有多大、阻尼是多少。你调参数全靠试,试到一组能用的参数可能要花几个小时甚至几天。
如果你有一个靠谱的模型,你可以先用仿真确定一组初始参数,然后在实际系统上微调。这个流程比盲调快得多。
之前做一个机械臂关节的控制,同事直接上PID盲调,搞了两天还是震荡。我花了一个下午测了关节的转动惯量和摩擦系数,建了一个简单的二阶模型,在仿真里算了一组参数,上去之后十分钟就调好了。建模不是浪费时间,是省时间。
从牛顿定律到微分方程
建模的第一步,是列物理方程。对于机械系统,最基础的工具就是牛顿第二定律:F=ma。
拿一个简单的弹簧-质量-阻尼系统来说。一个质量块m,连着一个弹簧(弹性系数k),连着一个阻尼器(阻尼系数b),你施加一个外力F(t)。
根据牛顿第二定律,对质量块列力平衡方程:
m*a = F(t) - k*x - b*v
其中a是加速度,v是速度,x是位移。把a写成x的二阶导数,v写成x的一阶导数:
# 弹簧-质量-阻尼系统的微分方程
# m*x'' + b*x' + k*x = F(t)
# 这就是一个二阶线性常微分方程
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
m = 1.0 # 质量 (kg)
b = 0.5 # 阻尼系数 (N·s/m)
k = 2.0 # 弹簧系数 (N/m)
def mass_spring_damper(t, y, F):
"""状态方程: y = [x, v]"""
x, v = y
dxdt = v
dvdt = (F - b*v - k*x) / m
return [dxdt, dvdt]
# 施加1N的阶跃力
sol = solve_ivp(mass_spring_damper, [0, 20], [0, 0],
args=(1.0,), max_step=0.01)
print(f"稳态位移: {sol.y[0, -1]:.3f}m")
这个微分方程就是系统的数学模型。它描述了输入F(t)和输出x(t)之间的关系。有了这个方程,你就可以分析系统的特性——响应速度、超调量、稳态误差。
状态空间表示法
上面那个二阶微分方程,可以拆成两个一阶微分方程,写成矩阵形式。这就是状态空间表示法。
选状态变量x1=x(位移),x2=v(速度),则:
# 状态空间模型
# x1' = x2
# x2' = (-k/m)*x1 + (-b/m)*x2 + (1/m)*F
import numpy as np
m, b, k = 1.0, 0.5, 2.0
A = np.array([[0, 1],
[-k/m, -b/m]]) # 系统矩阵
B = np.array([[0],
[1/m]]) # 输入矩阵
C = np.array([[1, 0]]) # 输出矩阵(观测位移)
D = np.array([[0]]) # 直通矩阵
# 状态方程: dx/dt = Ax + Bu
# 输出方程: y = Cx + Du
# 仿真
x = np.array([0, 0]) # 初始状态
u = 1.0 # 输入力
dt = 0.01
for i in range(2000):
x_dot = A @ x + B * u
x = x + x_dot * dt
print(f"稳态状态: 位移={x[0]:.3f}m, 速度={x[1]:.3f}m/s")
状态空间表示法的好处是通用——任何阶数的微分方程都能转化为一阶矩阵方程,计算机处理矩阵运算非常高效。而且多输入多输出系统也能用同样的框架描述,只需要扩展矩阵的维度。
模型简化:别追求完美
实际工程中,一个重要的原则是:模型够用就行,别追求完美。
你建一个极其精确的模型,考虑了摩擦非线性、弹性变形、温度效应、齿轮间隙,结果模型本身复杂到你根本没法用它设计控制器。这种模型除了发论文,没啥实际用处。
工程建模的核心思路是"抓主要矛盾"。对于低频运动,高频的动态特性可以忽略。对于小范围运动,非线性可以线性化。对于耦合很弱的通道,可以解耦成独立的单变量系统。
比如一个机械臂,完整的动力学模型包含科里奥利力、离心力、重力项,非常复杂。但如果你只关心单个关节在小范围内的运动,可以把其他关节锁定,把这个关节简化成一个弹簧-质量-阻尼系统。这个简化模型虽然不完美,但足够你设计一个能用的控制器了。
记住一个原则:控制器设计用的模型不需要完美,但需要抓住系统的主要动态特性。什么叫"主要动态特性"?就是决定系统响应速度和稳定性的那些因素——惯量、阻尼、刚度。次要因素(比如微小的非线性摩擦)可以先忽略,等控制器跑起来了再补偿。
之前面试一家做四足机器人的公司,面试官问"你怎么给机器人的腿建模"。我说先用简化的单腿模型(一个二阶系统)来设计控制器,然后在完整的多体动力学模型上验证和微调。面试官点头说这就是他们实际的做法。
系统辨识:当物理建模太难的时候
有些系统你很难用物理定律直接建模——比如一个液压系统、一个化学反应过程、甚至一个复杂的电路。这时候可以用系统辨识的方法:给系统施加已知的输入信号,测量输出响应,然后用数学方法拟合出模型参数。
最常见的做法是施加一个阶跃信号,记录系统的阶跃响应曲线,然后从曲线的特征(上升时间、超调量、稳态值)反推出模型参数。这个方法在工业界用得非常多,后面讲PID调参的时候还会提到。
# 简单的系统辨识:从阶跃响应估计一阶系统参数
# 假设系统是一阶的: G(s) = K / (tau*s + 1)
# 从阶跃响应中可以估计K和tau
import numpy as np
# 假设测量的阶跃响应数据
t_data = np.array([0, 0.5, 1.0, 1.5, 2.0, 3.0, 5.0, 10.0])
y_data = np.array([0, 0.39, 0.63, 0.78, 0.86, 0.95, 0.99, 1.0])
# 稳态增益K = y_ss / u_step
K = y_data[-1] / 1.0 # 假设阶跃幅值为1
# 时间常数tau: y(tau) = 0.632 * y_ss
# 从数据中插值找到y=0.632对应的时间
tau = np.interp(0.632 * K, y_data, t_data)
print(f"估计增益K={K:.2f}, 时间常数tau={tau:.2f}s")
面试的时候,如果面试官问"你怎么给一个未知系统建模",你可以说两条路:一是物理建模,从牛顿定律或基尔霍夫定律出发列方程;二是系统辨识,从输入输出数据拟合模型。两条路各有适用场景,实际工程中经常结合使用。
分享一个我在项目中做系统建模的经验。当时给一台差速移动机器人建运动学模型,一开始用理想差速运动学(两个轮子纯滚动无滑动),模型很简单但预测精度不够——机器人走直线时预测轨迹和实际轨迹偏差越来越大。排查后发现主要原因是轮子有轻微打滑,尤其是转弯时内侧轮会滑动。我们在理想模型基础上加了一个滑动补偿系数——通过实验测量不同转弯半径下的实际位移和理论位移的比值,拟合出一个补偿函数。加上补偿后,轨迹预测精度提升了约40%。这个经验告诉我,建模不能追求完美——先用简单模型跑起来,再根据实际数据逐步添加修正项,这种渐进式的建模方法在工程中最实用,面试时候讲出来也能体现你的工程思维。
下一篇讲传递函数——用拉普拉斯变换把微分方程变成代数方程,这是经典控制理论最核心的工具之一,也是面试中经常考到的知识点。
如果这篇文章对你有帮助,欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。
「机器人软件开发面试·从入门到精通」连载系列 上一篇:第177篇 反馈控制原理——闭环为什么比开环好 下一篇预告:第178篇 系统建模——从物理方程到数学模型
有任何问题欢迎评论区留言,我会尽量回复。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)