☰
线性时变模型预测控制(LTV MPC)设计与轨迹跟踪实战
2026/10/14 6:53:00 网站建设 项目流程

做控制的人大概都绕不开一个场景:给定一条参考轨迹,要让一辆小车、一台机械臂或者一辆车稳稳地跟上去,不超调、不震荡、还能把电机或液压的极限约束考虑进去。最早我用PID硬调,调得头疼;后来换成线性时变模型预测控制(LTV MPC),才发现“约束”和“预测”这两个词能组合出多大的优势。这篇内容就是围绕LTV MPC控制器设计,从理论到应用实现完整拆一遍,适合自动化、机器人、智能驾驶方向的工程师、研究生,以及所有被非线性系统跟踪控制难住过的人。

LTV MPC的全称是Linear Time-Varying Model Predictive Control,线性时变模型预测控制。说白了,就是把一条非线性轨迹上的每个时刻都当成一个局部线性工作点,构造出随时间变化的A(k)、B(k)矩阵,然后在每个采样周期求解一个带约束的二次规划问题。理论不绕,但真正从“看懂公式”到“跑出稳定跟踪效果”中间隔着很多实操细节,比如线性化怎么选点、预测矩阵怎么组装、QP求解超时怎么办,这些才是工程里真正卡住人的地方。

1. 先弄懂一件事:为什么控制问题非要用MPC

1.1 从“预测”说起:MPC的本质就三件事

MPC这个东西,第一眼看上去像“高级PID”,实际上底层逻辑完全不同。它每个控制周期都干三件事:预测未来、滚动优化、反馈校正。不理解这三件事,后面所有公式和参数都是空的。

预测是指用当前状态和模型,推算出未来N步的系统行为;滚动优化是指在预测的基础之上,求解一个让目标函数最小化的控制序列,但只取出第一步作用到系统上;反馈校正是等到下一个采样周期,重新测量状态、重新预测、重新优化。这个“每一步都重来一遍”的过程,就是所谓的滚动时域优化。它天然把“眼前的控制动作”和“未来的代价”绑定在一起,所以比PID更能提前规避超调和约束冲突。

类比一下生活场景:你开车过一个路口,看得远的人会提前减速、变道、预判交通灯,看得近的人只能到跟前急刹车。MPC是一个看得远的驾驶员,PID是一个只看眼前两三米的新手。虽然新手反应也很快,但路况一变复杂,处理能力和处理结果就不在一个层次上。

1.2 线性时变模型预测控制到底“时变”在哪

很多刚接触LTV MPC的人会问一个问题:MPC我会了,线性时变到底变的是什么?

要理解“时变”,得先理解“线性化”。大多数物理系统的模型都是非线性的,比如一辆AGV运动学的横纵向耦合方程、机械臂关节的动力学方程,写成连续时间形式全带三角函数和高阶耦合项。直接拿非线性模型做MPC,就要解非线性优化问题,在线计算压力很大。

工程上的折中方案是:在每个采样时刻,沿着参考轨迹上的工作点,对非线性模型做一阶泰勒展开,得到一个局部的线性模型。这个线性模型的系数矩阵A(k)、B(k)是随时间变化的,因为参考轨迹每个点的状态和控制量不同,雅可比矩阵的值也不同。于是就有了“线性时变”这个说法。

和LTI MPC(线性时不变MPC)的区别就在这里。LTI MPC在整个控制过程中只用一个固定模型,适合工作点基本不变的场景;LTV MPC每个采样周期都换一次模型,适合强非线性、大工作范围、沿复杂轨迹跟踪的场景。换一个更直白的说法:LTI是按一张老照片认路,LTV是每走一步都重新拍一张当前视角的照片再决定下一步。

1.3 LTV MPC的定位:比LTI强在哪、比NMPC省在哪

控制方案选型这件事情,其实没有绝对的好坏,只有适合与否。我把LTI MPC、LTV MPC、NMPC放在一起对比过多次,在轨迹跟踪这类问题里,LTV MPC的定位非常微妙——它是精度和计算量之间的甜点区。

LTI MPC的优点和缺点都很鲜明。模型固定,预测矩阵可以离线算好,在线求解很快,但一旦工作点偏离设计点,模型失配会让控制器表现迅速恶化。NMPC直接基于非线性模型做优化,理论上最精确,但在线要处理非线性优化问题,不仅迭代多、耗时长,而且对初值和求解器鲁棒性要求很高,嵌入式平台上很容易跑不起来。

LTV MPC走了一条中间路线:模型虽然是局部的,但每个周期都更新,所以能跟随非线性轨迹上的变化;优化问题仍然是二次规划(QP),是凸的,所以求解稳定、速度快。对于运动学控制级别的应用,LTV MPC基本能达到和NMPC接近的跟踪效果,计算量却小了一个量级,这是它在自主驾驶和移动机器人领域特别受欢迎的根本原因。

2. LTV MPC控制器设计的理论基础与建模

2.1 被控对象从哪来:运动学模型与动力学模型

做LTV MPC,第一步不是写代码,而是拿到被控对象的数学模型。模型可以粗略分成两类:运动学模型和动力学模型。

运动学模型描述几何关系,比如一辆车的位置和航向角如何随速度和转角变化。以最常见的自行车模型为例,状态取水平坐标x、垂直坐标y、航向角theta,控制量取速度v和前轮转角delta,连续时间模型是:

dot(x) = v * cos(theta) dot(y) = v * sin(theta) dot(theta) = v * tan(delta) / L

这里的L是轴距。这个模型简单直观,适合低速AGV、扫地机器人、园区无人车这一类运动控制场景。它的特点是状态维度低、计算量小,但模型没考虑惯性、轮胎侧偏、执行器延迟,所以高速和高动态场景不能指望它。

动力学模型则要考虑质量和力,典型形式是:

M(q) * ddot(q) + C(q, dq) * dq + G(q) = tau

这类模型用来描述机械臂、无人机、高速车辆等系统。动力学模型精度更高,但线性化之后矩阵维度更大,求解QP的规模也随之变大。LTV MPC帮不上动力学模型的忙,因为它和模型本身无关,和模型复杂度有关。模型越复杂,预测矩阵越复杂,在线计算负担越重。

我的建议是:先根据你的应用场景选模型类型。低速移动平台用运动学模型,验收标准是“轨迹能不能跟住”;中高速或动态性能要求高的系统再用动力学模型,并且需要有足够的控制器算力支持。

2.2 线性化与离散化:每一步都在换模型

拿到连续非线性模型之后,LTV MPC的下一步就是对它做线性化和离散化。这步处理直接决定预测模型准不准,也决定后面QP问题能不能稳定求解。

线性化的常用做法是定义偏差量。设参考轨迹在某时刻的状态为x_ref,控制量为u_ref,定义偏差量:

x_tilde = x - x_ref u_tilde = u - u_ref

然后把非线性模型在当前工作点沿着参考值做一阶泰勒展开,得到连续时间线性时变模型:

dot(x_tilde) = A_c(t) * x_tilde + B_c(t) * u_tilde

其中A_c(t)和B_c(t)是系统关于状态的雅可比矩阵和控制量的雅可比矩阵。还是在自行车模型这个例子上展开,A_c和B_c写成矩阵形式是:

A_c = [[0, 0, -v_ref * sin(theta_ref)], [0, 0, v_ref * cos(theta_ref)], [0, 0, 0]] B_c = [[cos(theta_ref), 0], [sin(theta_ref), 0], [tan(delta_ref)/L, v_ref / (L * cos(delta_ref)^2)]]

离散化这一步同样关键。工程中最常用的是一阶前向欧拉离散,采样时间为Ts:

A_d(k) = I + Ts * A_c(k) B_d(k) = Ts * B_c(k)

注意这里说的“每个采样时刻重新计算A(k)和B(k)”,不只是一个理论概念,而是工程必须做的事情。参考轨迹上每个点的状态和控制量不同,雅可比矩阵的值就不同,A_c和B_c是实时变化的。控制器在每个采样周期都必须根据当前参考点重新求一次雅可比、重新离散化。这也是“线性时变”在代码里的直接体现——每一步都在换模型。

2.3 预测方程的矩阵化组装

有了离散LTV模型之后,就要构造预测方程。所谓预测方程,就是用当前状态偏差量x_tilde(k)和一组未来的控制偏差量u_tilde(k), u_tilde(k+1), ..., u_tilde(k+N-1),推算未来N步的状态偏差量。

如果时间范围内所有A_d和B_d都保持不变,即采用恒定LTI近似,预测方程非常规整:

X_pred = Phi * x_tilde(k) + Psi * U_tilde

其中:

Phi = [A_d] [A_d^2] [A_d^3] [...] [A_d^N] Psi = [B_d, 0, 0, ...] [A_d*B_d, B_d, 0, ...] [A_d^2*B_d, A_d*B_d, B_d, ...] [...] [A_d^(N-1)*B_d, A_d^(N-2)*B_d, ... , B_d]

N是预测时域。Phi和Psi分别表示了初始状态偏差量和未来控制输入对预测状态的影响权重。X_pred是一个Nn维的列向量,U_tilde是一个Nm维的列向量。

如果严格考虑时变模型,矩阵要逐块代入,也就是每一步用对应的A_d(i)和B_d(i)做累积连乘。这个展开方式在理论推导上很严谨,但代码实现上要把A_d列表和B_d列表保存起来,再用循环组装,非常容易把行列维度弄错。后面讲代码的时候,我会给出一个更工程化的“展开式”写法,它和显式组装Phi、Psi在数学上等价,但代码直观得多,不易出错。

2.4 目标函数与约束的标准化

LTV MPC的优化目标一般设计成二次型,常见的离散形式是:

J = sum_{i=0}^{N-1} [ x_tilde(k+i+1)^T * Q * x_tilde(k+i+1) + u_tilde(k+i)^T * R * u_tilde(k+i) ]

Q是状态权重矩阵,R是控制量权重矩阵。Q大意味着更强调跟踪精度,R大意味着更不想让控制量波动剧烈。你可以把Q想象成“看路认不认真”,把R想象成“打方向盘轻不轻”。

把目标函数和预测方程代入整理,二次项和控制量有关的项可以被标准化成QP标准型:

min 1/2 * U_tilde^T * H * U_tilde + g^T * U_tilde

其中:

H = Psi^T * Q_bar * Psi + R_bar g = Psi^T * Q_bar * Phi * x_tilde(k)

Q_bar是Q在预测时域内重复组成的块对角矩阵,R_bar同理。这一步的意义在于,目标函数被整理成了纯关于决策变量U_tilde的凸二次函数,可以直接交给QP求解器处理。

约束部分一般考虑三类:控制输入本身的幅值约束、控制输入的变化率约束、状态量约束。写成统一的不等式形式:

A_ineq * [u_tilde(k), ..., u_tilde(k+N-1)] <= b_ineq

幅度约束表示速度和转角不能超过机械极限;变化率约束表示执行器每秒能变化的最大值,防止给伺服系统下达过猛指令;状态约束通常用于安全性,比如距离障碍物不能低于某个值。所有约束都基于偏差量表示,计算的时候要记得把参考值带回去。

有一点特别提醒:模型预测控制最值钱的能力不是“预测”本身,而是“把约束放进优化里”。这也是它和LQR最核心的区别。LQR解出来的是一个状态反馈律,没有约束处理机制;LTV MPC每一次滚动优化都在约束下找最优,这让它在工程现场特别能打。

3. 从理论到代码:一个AGV轨迹跟踪控制器的完整设计

3.1 场景设定与控制目标

为了不空谈理论,我设计了一个贯穿式的应用实例。场景是一台差速驱动改造的阿克曼转向AGV,在厂区地面上沿着一条由若干关键点组成的参考轨迹行驶。控制周期是0.1秒,最大线速度限制在1.2米每秒,最大前轮转角限制在正负0.35弧度。

控制目标很简单:横向距离偏差保持在正负10厘米以内,航向角偏差保持在正负3度以内,同时保证转向动作平滑,不出现高频抖动。

我选这个场景的理由是它足够典型。你在论文里看到的路径跟踪、车道保持、轨迹规划落地,绝大多数本质都是这样一个状态偏差调节问题。场景复杂度不高不低,刚好可以把LTV MPC的全部设计流程走一遍。

3.2 关键模块划分与数据结构

控制器代码我习惯拆成四个模块:参考轨迹生成器、模型线性化器、预测与优化器、控制量执行器。模块划分越清晰,后面调试定位越容易。很多项目最后失控,不是因为MPC原理不会,而是因为代码里参考量、偏差量、真实量混在一起,谁和谁对不上。

数据结构上我通常定义三类:参考轨迹点RefPoint,包括参考状态x_ref、y_ref、theta_ref和参考控制量v_ref、delta_ref;当前状态CurState,包括实时反馈的位置和航向角;决策变量序列ControlSequence,缓存最近一次QP求解结果中的最优控制序列。注意控制序列缓存不只是给执行器用的,它还可以作为下一次求解的热启动初值,能明显缩短求解器迭代时间。

这里我还想强调一下参考控制量的来源。很多初学的人只给了一条几何轨迹,没有给对应的参考控制量,导致线性化时缺了u_ref,偏差量也定义不了。正确做法是在轨迹规划阶段同时生成运动学可行的速度规划和转向规划,或者是用上一时刻的控制器输出作为参考控制量的近似。后者在某些平滑轨迹上也能工作,但不如前者稳。

3.3 核心代码实现:模型构建与QP求解

我直接给出一个可读性优先的Python实现。这里使用开源的科学计算和优化库来搭框架。为了不引入太多依赖,我聚焦在线性化、预测展开和QP求解三个关键环节。

先写组成部分:一个生成预测模型序列的函数,一个展开预测并构建优化问题的函数。

import numpy as np def compute_ltv_model(x_ref, y_ref, theta_ref, v_ref, delta_ref, L, Ts): # 计算当前工作点处的连续时间雅可比矩阵 Ac = np.array([ [0.0, 0.0, -v_ref * np.sin(theta_ref)], [0.0, 0.0, v_ref * np.cos(theta_ref)], [0.0, 0.0, 0.0] ]) Bc = np.array([ [np.cos(theta_ref), 0.0], [np.sin(theta_ref), 0.0], [np.tan(delta_ref) / L, v_ref / (L * np.cos(delta_ref)**2)] ]) # 前向欧拉离散化 Ad = np.eye(3) + Ts * Ac Bd = Ts * Bc return Ad, Bd

这个函数每被调用一次,就根据当前参考轨迹点生成一组A_d和B_d。预测时域内的每一组系数,我习惯预先算好存成一个列表。这样既保留了LTV模型的时变性,又让后续预测计算逻辑非常简单。

再写预测展开和优化构建。这里没有显式组装Phi和Psi,而是采用循环展开强制约束。这种写法的好处是代码逻辑和手写推导完全一致,维度不会出错。

import cvxpy as cp def solve_mpc(cur_state, ref_traj, model_list, N, Q, R, u_min, u_max): n = 3 m = 2 # 状态偏差决策变量,形状为 (n, N+1) x_tilde = cp.Variable((n, N + 1)) # 控制偏差决策变量,形状为 (m, N) u_tilde = cp.Variable((m, N)) # 初始条件:当前状态与参考轨迹起点之差 init_error = cur_state - ref_traj[0] cost = 0.0 constraints = [x_tilde[:, 0] == init_error] for i in range(N): # 循环展开预测方程:x(k+i+1) = Ad(i) * x(k+i) + Bd(i) * u(k+i) Ad_i, Bd_i = model_list[i] constraints.append( x_tilde[:, i + 1] == Ad_i @ x_tilde[:, i] + Bd_i @ u_tilde[:, i] ) # 目标函数累加 cost += cp.quad_form(x_tilde[:, i + 1], Q) + cp.quad_form(u_tilde[:, i], R) # 控制量幅值约束,注意偏差量要加回参考值 for i in range(N): u_real = u_tilde[:, i] + ref_traj[i][3:5] constraints.append(u_real <= u_max) constraints.append(u_real >= u_min) prob = cp.Problem(cp.Minimize(cost), constraints) prob.solve(solver=cp.OSQP, warm_start=True) if prob.status not in ["optimal", "optimal_inaccurate"]: return None # 只取第一步真实控制量 real_u_first = u_tilde[:, 0].value + ref_traj[0][3:5] return real_u_first

这段代码如果直接跑,需要注意几个变量约定。ref_traj里我约定每行元素依次是x_ref、y_ref、theta_ref、v_ref、delta_ref,所以在代码里取了[3:5]作为参考控制量。model_list是预测时域内所有步的Ad、Bd集合,由compute_ltv_model针对每个参考点调用生成。cur_state是当前实测状态。

用CVXPY的好处是二次规划问题描述简单,问题是规模稍大或算力受限时,用它做实时控制器不一定够快。嵌入式落地一般直接用OSQP或者qpOASES这类专为QP设计的求解器,CVXPY在这里作为原型验证和方案讨论的载体是够用的。

3.4 实测调参记录与参数选择经验

参数选择没有万能公式,但有可以遵循的经验范围。下面是我在这类AGV场景里常用的初始参数,以及它们调整的规律。

参数含义初始值调参方向
Ts采样时间0.1s太长会丢动态信息,太短会放大噪声
N预测时域20车身响应慢就加大,计算超时就减小
Q状态权重diag([1.0, 1.0, 0.1])位置偏差大就加大前两维
R控制权重diag([0.1, 0.1])抖动明显就加大,但要接受跟踪变慢
u_max控制上限[1.2, 0.35]由机械极限决定,不能为跟踪而放宽

N的选择其实有个指导原则:预测时域应该覆盖系统动态主导时间常数。对这辆AGV,横摆动态的响应时间大约在0.8到1秒之间,采样周期0.1秒时N取10到20就比较合适。N取太大不仅计算量上去了,预测模型误差的累积也会让求解结果失真,并不是越大越好。

我实际调Q和R的经验是先固定N和Ts,只调Q、R。如果轨迹跟踪响应慢,就增大Q;如果到位后还来回调整,大概率是R设得太小,控制器过于激进。另外一个容易被忽略的点:Q里航向权重不能设得太大。航向权重一大,小车会优先纠正角度而不是贴近轨迹,最后走出来的路径反而会偏离参考线。

整个闭环跑起来之后,横向偏差实测基本稳定在正负5厘米内,转向指令也平滑。这个结果在低速AGV场景里完全够用。如果参考轨迹曲率突变特别剧烈,LTV MPC的线性化假设在曲率跳变点附近误差会放大,这时要把N适当拉短、Ts适当缩小,让模型“喂”给优化器的信息更贴近当前工况。

4. 把控制器从仿真搬到实物:常见问题与排查技巧

4.1 仿真很稳,实车乱抖是怎么发生的

仿真环境里模型完全自洽,控制器表现好是正常的。真正考验人的是实车验证阶段,仿真跑出来的参数直接搬上去,大概率会出问题。最常见的就是方向抖动,轨迹上一会儿偏左一会儿偏右,或者速度指令高频震荡。

这件事的根子通常不在MPC本身,而在“模型和实际的差距”。仿真里的执行器响应是即时完美的,实车有执行器延迟、有转向电机惯性、有反馈噪声。前轮转角指令变化率约束没有设好,控制器自然会让执行器做高频大幅度运动。

排查时先看两个东西:第一,实测状态反馈有没有经过滤波。位置给的是轮式里程计加上IMU融合出的状态,原始数据噪声一旦进入偏差量计算,QP求解出的控制序列也会被污染,表现在输出上就是抖。第二,控制指令输出到执行器之后有没有做平滑处理。最直接的办法是在目标函数中增加变化率终端约束,把相邻两步控制量偏差的平方加进代价函数:

J_rate = w_rate * sum( (u(k+i+1) - u(k+i))^2 )

这个代价项本质上是给转向指令加一个“阻尼”,效果远比输出滤波来得干净。加了这项之后,我实际遇到的高频抖动基本消失了,横向偏差稍微变大了一点,但完全在允许范围内。

4.2 约束不可行与求解超时的处理

QP求解器返回问题不可行(infeasible),是LTV MPC初学者遇到最多的状况。看到这个报错,先不要慌,核心原因基本逃不开三类:约束互相矛盾、参考轨迹本身执行不了、某个状态约束定得比物理极限还紧。

比如把最大转角设为0.35弧度,但某个参考轨迹点的规划转角就达到0.4弧度,那这个点在约束层面就是无解的。解决办法有两种:一种是在轨迹规划阶段保证规划的参考控制量都在约束范围之内,这是最彻底的做法;另一种是在MPC里对约束做软化处理,也就是增加松弛变量,允许在某些极端工况下小幅突破约束,同时在目标函数里对松弛量加大惩罚。软约束的工程价值很大,它能让控制器在模型失配或轨迹劣化时退回“能走就行”模式,而不是直接报错停车。

求解超时是另一个高频问题。出现超时,首先要复查预测时域N是否过大。N超过一定值,QP矩阵的维度会非线性增长。处理优先级我建议这样排:第一,缩小N;第二,启用热启动,也就是把上一个控制周期的解作为本轮求解的初值;第三,检查约束条件数量,很多多余约束是可以删除的,比如状态量本身物理不可能越界的约束就不要加进QP。实测下来,常用参数下单次QP求解时间能稳定控制在10毫秒以内,而一个控制周期是100毫秒,预算非常宽裕。

4.3 数值病态与归一化处理

LTV MPC在工程中还有一个隐蔽的坑,叫数值病态。状态量的量纲差异太大的时候,QP求解精度会下降,甚至出现“看起来在收敛、实际跟踪效果很怪”的假象。举个例子,一辆AGV的位置x、y的量纲是米,量级在几到几十之间,而航向角的量纲是弧度,量级只有零点几到几。把这两个量直接放进一个代价函数,Q矩阵里的权重就很难设计,求解器内部的数值范围也可能被拉得很差。

好的工程实践是先做状态归一化或者尺度化。位置偏差除以最大允许偏差,角度偏差除以最大允许角度,得到相对偏差量,再进入目标函数。控制量同样做尺度化,速度除以最大速度,转角除以最大转角。这样可以保证QP内部的矩阵元素处于同一量级,求解器数值稳定性会明显提升,调Q、R时也更有感觉。

另外,矩阵求逆或连乘尽量避开手工运算。预测方程展开时,如果使用显式的Phi和Psi矩阵,要定期检查矩阵的条件数。条件数过大时不要硬用,回退到循环展开方式,数值上更稳。

4.4 常见问题速查表

我把自己和身边同事踩过的问题整理成一张速查表,能解决大多数现场排查需求。

现象最常见原因首选处理手段
控制高频抖动约束或代价中缺少控制变化率惩罚增加相邻控制量差的代价项
跟踪偏差总是偏大Q权重太小或预测时域N太短增大Q而不是盲目加大N
轨迹转弯处明显偏移线性化点滞后于轨迹更新检查参考量是否使用最新时刻
QP求解返回不可行约束太紧或参考轨迹不可行检查参考控制量是否越过约束
同一套参数换个轨迹就跑飞模型泛化能力不足检查模型是否覆盖新轨迹的曲率范围
求解时间忽长忽短求解器初始点不稳定开启热启动并固定初始解策略
状态反馈噪声大导致指令毛刺反馈信号未滤波对偏差量做低通滤波或前端融合

最后分享一个我至今觉得最值钱的小技巧:LTV MPC调试时不要一上来就盯全场轨迹误差,把参考轨迹换成一个简单直线或大半径圆,先把控制器跑稳,再逐步改成复杂轨迹。每换一档复杂度,只调一个参数。我之前图省事直接上一段双移线,结果参数相互牵扯,调了整整两天没定位到根因;换回简单轨迹后,半小时就锁定了问题出在航向权重过高。

LTV MPC在工程里的定位我越来越认同:“中间态”三个字恰恰是它最实用的地方。你要绝对精度和极限工况表现,可以上NMPC;你要极低算力且工作点固定,LTI MPC也够用;但在广阔的日常场景里,既需要非线性曲线上跟踪,又要求实时计算稳定,线性时变模型预测控制几乎就是最优解。经历过几次从仿真到实物的折腾之后,我的体会是,掌握了这套建模和调试方法,以后换平台、换模型都只是换矩阵维度的事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询