01先从一个简单的控制问题开始

怎样让小车,停在指定位置?

斜坡让它往回滑,惯性让它冲过头。
先只用 P:差得越远,就推得越大。

同一斜坡 · 同一目标
P 控制下的小车在斜坡上接近目标位置 动画展示小车的位置、目标、动力方向和距目标的偏差。下方可以比较 P、PI 与 PID。 起点 目标位置 下滑方向 向前推

斜坡一直把小车往回拉。先观察它最后停在哪里,再加上 I,看看能否消除这段偏差。

如果要让小车更快、更准、更稳地到达目标位置,还能怎么办?
一个思路是:先预测动作的结果,再决定怎样控制。

02从运动到模型

先预测动作的结果。

先看一个具体场景:小车经过起点时,正以 2.4 m/s2.4\,\mathrm{m/s} 向上坡行驶。就在这一刻松开油门,它会继续走多远,又会在什么时候滑回来?

上坡减速到达最高点向下回滑
m=1 kgm=1\,\mathrm{kg}
位置 pp:看地面的距离尺。速度 vv:看箭头的方向与长度。

① 先算合力,再算加速度。

加速度 aa 描述速度如何变化。用 FF 表示油门产生的驱动力。驱动力减去重力沿坡分力与阻力,就是合力;合力除以质量,得到实际加速度。

m⋅am\cdot a质量 × 实际加速度 == FF驱动力 −- m⋅g⋅sin⁡(θ)m\cdot g\cdot\sin(\theta)重力沿坡分力 −- b⋅vb\cdot v阻力项

质量记为 mm;居中的“·”是乘号。

在最高点,速度 v=0v=0,但加速度仍向下,所以小车会滑回来。

这条坡约 4.7°,g⋅sin⁡(θ)=0.8 m/s2g\cdot\sin(\theta)=0.8\,\mathrm{m/s^2}。松开驱动后,重力仍然在下拉。阻力系数 b=0.15 N⋅s/mb=0.15\,\mathrm{N\cdot s/m}。

② 加速度,决定速度如何变化。

速度不是一下跳到某个值,而是随加速度不断改变。符号上面的点,表示“每秒变化多少”。

v˙=a\dot{v}=a

③ 速度,决定位置如何变化。

速度越大,同样一段时间走得越远。把这些变化沿时间累积,就能算出未来的位置。

p˙=v\dot{p}=v
也可以写成状态空间模型(可选)

把位置和速度放进状态向量,就能把上面三条关系写在一起。

x=[pv]\mathbf{x}=\begin{bmatrix}p\\v\end{bmatrix} x˙=A⋅x+B⋅F+c\dot{\mathbf{x}}=A\cdot\mathbf{x}+B\cdot F+\mathbf{c}
A=[010−bm]A=\begin{bmatrix}0 & 1\\0 & -\dfrac{b}{m}\end{bmatrix} B=[01m]B=\begin{bmatrix}0\\\dfrac{1}{m}\end{bmatrix} c=[0−g⋅sin⁡(θ)]\mathbf{c}=\begin{bmatrix}0\\-g\cdot\sin(\theta)\end{bmatrix}

实际加速度 aa 是第二行的结果,会随驱动力 FF、质量 mm 和速度 vv 改变。

03你给动作,模型算未来

油门多大,踩多久?

拖动油门块,设定驱动力和松开时刻,看看模型预测的小车运动。

拖动驱动条,改变未来
0.00 / 10.00 s

实色:现在 · 淡色:未来每 0.1 秒的一帧,越远越淡。距离刻度固定。

Input你施加的动作
驱动力 FF 4.0 N 松开时刻 1.0 s

↑↓ 拖圆点,改驱动力←→ 拖标记,改时长

State小车的起始条件
kg
m
m/s

试试:保持油门不变,只把质量翻倍。

两栏的修改都会实时更新未来。
位置 p(t)
0.00m
速度 v(t)
0.00m/s
实际加速度 a(t)
—m/s²

给定驱动力与初始状态,模型就能预测实际加速度、速度和位置。

未来 10 秒的状态每隔 0.1 秒一帧 · 初始预测共 100 帧
近 · 0.1 s2 s5 s远 · 10 s黑点:当前播放时刻

预测位置 p(t) · m

预测速度 v(t) · m/s

现在点曲线或拖动时间,查看任意时刻10 秒后

按 1 倍速播放:动画中的 1 秒,对应模型中的 1 秒。虚线和彩色点是未来,深色线是已播放的部分。

编辑动作与初始状态,查看未来的运动。

这就是 Model:把动作、当前状态和物理关系,变成对未来的预测。

现在,我们能算出动作的后果了。
接下来要找的,是一段能让小车在目标处停稳的动作。

模型假设与来源

这里是沿斜坡方向的一维动力学模型,包含重力沿坡分力和与速度成正比的阻力,忽略轮子转动惯量与静摩擦。上述方程采用的参数为:g = 9.81 m/s²,θ ≈ 4.7°,b = 0.15 N·s/m。驱动力 F 在读者选定的时间段内生效,之后变为 0,仍按同一动力学演化。图中的位置、速度、加速度都从这一方程计算;驱动结束时切换输入,加速度会发生跳变。

这一步让读者自由指定短促驱动,暂不加电机上限。前面的 PID 示例采用 ±5 N 的控制输出限幅。示意图的坐标映射保持固定,不随速度或驱动变化。超出视野的运动仍可在曲线中查看,车的图标不代表实际尺寸。

PID 背景参考:密歇根大学:PID 控制器设计。页面图形与交互为原创。

04Optimization · 先优化一次

给定目标,让模型替你选动作。

刚才由你指定驱动力,现在把停车目标 p⋆p_\star 交给优化器。它调整未来 10 秒里每一小段的驱动力,用模型预测结果,寻找能到达目标并停稳的方案。

(p−p⋆)2(p-p_\star)^2到达目标
v2v^2停稳
F2F^2省油

把未来各时刻的这些代价加起来,分数越低,方案越好。这里用 F2F^2 简化表示油耗。驱动力限制在 −6 到 +6 N。

对照方案:4 N 推 1 秒,再松开。质量 1 kg · 从 0 m、速度 0 开始
0.00 / 10.00 s
这一整段驱动力 F(t)F(t)每一段持续 0.1 秒 · 共 100 段
10 秒后的预测位置—
10 秒后的预测速度—
方案代价 · 越低越好—

先播放对照方案,再让优化器试一试。

优化器选的是一整段动作:先加速、再制动,停车时仍需推力抵消斜坡上的重力。少踩油门与更快、更准地停车之间,需要取舍。这一节先假设模型和初始状态准确,求解一次,再执行整段方案。

优化目标怎样写成数学?

质量 1 kg,从位置 0、速度 0 开始。每段驱动力固定 0.1 秒,未知量是一串 F0,…,F99F_0,\ldots,F_{99}。位置与速度由前面的动力学模型算出。

min⁡F0,…,F99  ∑k=0990.1[20⋅(pk+1−p⋆)2+2⋅vk+12+0.08⋅Fk2]\min_{F_0,\ldots,F_{99}}\;\sum_{k=0}^{99}0.1\left[20\cdot(p_{k+1}-p_\star)^2+2\cdot v_{k+1}^2+0.08\cdot F_k^2\right]

最后一个时刻另外加入 50⋅(p100−p⋆)2+10⋅v100250\cdot(p_{100}-p_\star)^2+10\cdot v_{100}^2,并要求每段 −6≤Fk≤6  N-6\leq F_k\leq 6\;\mathrm{N}。Fhold=m⋅g⋅sin⁡(θ)=0.8  NF_{\mathrm{hold}}=m\cdot g\cdot\sin(\theta)=0.8\;\mathrm{N} 是抵消重力所需的力。权重决定取舍,并不存在适合所有任务的唯一评分方式。

这里使用解析离散化后的同一模型,求解带驱动力上下限的凸二次规划。界面中展示的是实际求解得到的动作序列。

05从一次优化,到实时控制

算好以后,小车被推了一下呢?

模型预测的是一种未来,真实运动可能被外力改变。让两辆小车从同一状态出发,使用同一份初始方案;第 3 秒,给它们同一次向上坡的推动。

前 3 秒没有推动;观察两辆车先按计划停车。

右侧小车:放大方案的前 0.3 秒下一步:—
执行第一步—0–0.1 s
预测—0.1–0.2 s
预测—0.2–0.3 s
完整的 10 秒预测方案时间从当前规划时刻起算

每次只执行深色的第一段 0.1 秒。其余动作是预测方案,下次读到新状态,就重新求解。

读当前状态→预测并优化→执行第一小步→再读状态

这就是 MPC:Model Predictive Control,模型预测控制。
每次仍向前预测 10 秒,但只执行最前面的 0.1 秒。预测窗口随小车的运动不断向前滚动。

扰动、传感器与这个演示

两辆车使用相同的模型、目标、驱动力范围与初始优化结果。推动在同一时刻给两车增加 0.8 m/s 的速度,不在原预测里;左侧继续执行旧方案,右侧每 0.1 秒用当前状态重新求解同一个优化问题。图上展示的是模拟运动。

这里使用准确的模拟状态,便于单独观察外部扰动。在真实机器人上,位置和速度来自传感器与状态估计;传感器噪声需要结合滤波或状态估计处理,重新优化本身不等于消除噪声。模型误差、驱动力限制和计算时间也会影响控制效果。

原理参考:do-mpc:模型预测控制基础。