在某个自动驾驶相关项目里做轨迹跟踪控制,当时从规划模块拿到一条参考轨迹,需要在仿真里让车稳稳地贴上去。用PID试了一圈,参数调到不想再调,弯道里还是存在肉眼可见的稳态误差。后来换成了基于LQR最优控制算法的轨迹跟踪控制方案,把质心侧偏角、横摆角速度、横向误差这些状态量统一放进状态空间里设计反馈律,效果才真正稳定下来。这篇文章就把我当时从建模、控制器推导到调参落地的完整过程整理出来,重点讲清楚每个环节背后的“为什么”,以及那些文档里不会写、只有跑仿真和试车才会碰到的坑。
如果你是刚接触车辆横向控制,或者是已经在用纯跟踪、PID但被弯道精度折磨的工程师,这篇内容应该对你有帮助。LQR不会像MPC那样占用大量算力,控制性能和可解释性都够用,是目前做车道保持、轨迹跟踪最划算的切入方案之一。
1. 轨迹跟踪控制的本质:我们到底在控制什么
1.1 跟踪的是路径,稳定的是误差
轨迹跟踪控制是一个典型的“让输出跟随外部参考”的问题。给定参考轨迹,每一时刻都有参考位置、参考航向和参考曲率。车辆作为一个带横摆动力学约束的刚体,不可能像编辑器里拖动对象一样一步到位,只能通过前轮转角来缓慢修正车与参考点之间的偏差。
把这个偏差拆开,就是标题里提到的几个量:横向误差是车辆质心到参考轨迹最近点的垂直距离;横摆角误差是车辆当前航向与参考点切线方向的夹角;横摆角速度就是车绕竖直轴的角速度,它决定了车辆航向变化的快慢。这些量一辆接一辆地构成了一条“误差链条”:
- 横向误差大,说明车离目标线远;
- 横摆角误差大,说明车没沿着切线走;
- 横摆角速度控制不好,说明航向的变化率不稳定,容易带来摆动。
所以轨迹跟踪控制的核心命题并不是“让车去哪里”,而是“让误差尽快收敛到零,且保证稳态行为合理”。这也是LQR这类基于状态空间的方法比PID更适合的核心理由:PID本质上只能对单个误差量做反馈,而LQR可以把多个相互耦合的状态量放在同一个代价函数里统筹优化。
1.2 LQR在整套控制链里的定位
LQR的全称是线性二次型调节器,它的工作建立在状态空间方程上。车辆模型是非线性的,轮胎力也随工况变化,但在小角度、中等车速、侧向加速度不高的情况下,可以把车辆近似成线性系统。这一近似并不是偷懒,而是汽车控制里的常规做法,线性化后的模型足以描述高速公路转弯、变道、低速泊车等大多数常规场景。
LQR和纯跟踪这类几何方法的最大区别是,几何方法只解决“当前该往哪打方向”的问题,而LQR同时考虑了误差、误差变化率、车辆动力学状态的协同收敛,并在设计阶段就把控制代价考虑进去。这就意味着,它天然具有“回忆”和“预判”能力,而不是纯粹盯着当前误差做反应。当然,LQR也不是万能的,当轮胎侧偏角进入非线性区、车辆接近物理极限时,LQR会因为模型失配而明显衰减。这一点后面我会专门展开。
2. 误差状态空间方程:从物理车辆到矩阵表达
2.1 自行车模型的假设与边界
做LQR控制最常用的整车模型是自行车模型,也常叫作二自由度模型。它假设车辆左右两侧合并成单轮模型,忽略侧倾、俯仰和垂直载荷转移,只关注横向运动和横摆运动的耦合。
模型的物理参数不复杂:质量m、横摆转动惯量Iz、轴距L、前轴到质心距离lf、后轴到质心距离lr、前轮侧偏刚度Cf、后轮侧偏刚度Cr。在这个模型下,车辆的运动状态由质心侧偏角β(质心速度方向与车身纵轴的夹角)和横摆角速度γ(航向角的变化率)描述,它们直接构成了车辆动力学的基础层。
我这里用工程上更常用的误差状态表达,状态向量取X = [ey, e_psi, ey_dot, e_psi_dot](横向误差、横摆角误差、横向误差变化率、横摆角误差变化率)。注意,标题里出现的质心侧偏角和横摆角速度并没有被丢掉,它们通过动力学方程隐含在状态转移矩阵中:横向和横摆方向的受力平衡,本质上就是β和γ的动态方程在起作用。如果想更显式地把它们作为控制状态,也可以构建X = [ey, e_psi, β, γ]这样的五阶或四阶状态空间,这在学术论文里很常见,工程上同样可解。两种建模之差只是看问题的角度,控制方法不变。
2.2 误差动态方程的推导思路
误差状态空间方程通常写成:
e_dot = A * e + B * delta其中A矩阵中的元素来自车辆参数和纵向车速v。比如横向误差的导数关系可以写成:
ey_dot = v * e_psi + ...这里的v是纵向车速,e_psi是横摆角误差。横摆误差的导数和参考横摆角速度有关,而参考横摆角速度由当前轨迹的曲率决定:r_ref = v * kappa。参考曲率一般来自规划轨迹,每个时刻都能直接查得。
B矩阵描述的是前轮转角delta对误差动态的直接作用。前轮转角通过轮胎侧偏力作用在车辆上,改变横向力平衡和横摆力矩平衡,因此既会影响横向误差的加速度,也会影响横摆角速度的变化率。
我建议你在自己的实现里不要直接抄书本上某个固定矩阵,而是花一点时间从自行车模型受力平衡推导一遍,哪怕推导过程中符号定义不同,至少你能清楚每一项的物理来源,后面调参、诊断异常现象时会轻松很多。
2.3 为什么说“状态里带了耦合,控制才有全局观”
LQR能把横向误差、横摆角误差、横摆角速度等变量一起考虑,是因为这些量本来就是一个系统的不同侧面。如果只对横向误差做PID,你会发现高速弯道里横向误差刚收敛,横摆角误差可能已经发散;如果一起反馈,控制器知道当前横摆角误差很大、横向误差还在小量阶段,就会提前给出一个恢复航向的转角,而不是等横向误差大了再反应。
这个特性在实际造价时的感受特别明显:横向误差和横摆角误差就像一对互相拉扯的弹簧,单一回路压住一个,另一个就会反弹。只有把它们的动态放在一个状态空间里,同时由同一个增益矩阵协调,系统的瞬态行为才可能平稳。
3. LQR控制器设计全流程:从目标函数到增益矩阵
3.1 二次型目标函数:惩罚状态、也要惩罚控制量
LQR的设计起点是一个目标函数:
J = ∫ (X^T Q X + δ^T R δ) dt这个式子的意思很直白:运行过程中,让状态偏差和控制量的加权平方和尽可能小。Q矩阵的每个对角元素对应一个状态量的“惩罚权重”,R则对应控制量的惩罚权重。
以我常用的初始值举例:
| 参数 | 初值 | 说明 |
|---|---|---|
| Q[ey] | 10~20 | 位置误差必须严厉惩罚 |
| Q[e_psi] | 50~100 | 航向误差权重偏高,防止航向发散 |
| R | 1~2 | 前轮转角惩罚,数值小代表控制翻天覆地不心疼 |
| 采样周期 | 10~20ms | 离散化控制周期 |
为什么横摆角误差的权重往往要大于横向误差?因为横向误差的收敛依赖航向准确,如果横摆角误差不控制好,车辆即使当前贴近轨迹线,也会在下一时刻快速脱离。而控制量R的取值关系到方向盘的“激进程度”:R太小,控制器会频繁调整前轮转角,路面稍有激励就震荡;R太大,响应迟钝,弯道恢复慢。
3.2 黎卡提方程和反馈增益的计算链路
连续时间LQR的解法是通过代数黎卡提方程求解一个对称正定矩阵P:
A^T P + P A - P B R⁻¹ B^T P + Q = 0求出P之后,反馈增益矩阵K由下面的式子得到:
K = R⁻¹ B^T P最终的控制率为:
δ = -K X注意,实际工程里我们很少直接求解连续黎卡提方程,因为控制器以固定周期运行,状态方程也是离散的。更常见的做法是把连续状态空间方程做零阶保持离散化,再调用离散黎卡提方程求解函数,计算出离散增益K。这也是你如果直接看某工具箱里的LQR函数,会发现它给的输入输出和教科书上有微妙差别的原因。
我提供一段可直接运行的参考代码,用的是Python环境下常见的数值计算库:
import numpy as np from scipy.linalg import solve_discrete_are def compute_lqr_gain(A, B, Q, R, dt): # 零阶保持离散化 n = A.shape[0] A_d = np.linalg.inv(np.eye(n) - A * dt / 2) @ (np.eye(n) + A * dt / 2) B_d = B * dt # 离散黎卡提方程 P = solve_discrete_are(A_d, B_d, Q, R) # 离散LQR增益 K = np.linalg.inv(B_d.T @ P @ B_d + R) @ (B_d.T @ P @ A_d) return K这段代码里我用了一阶双线性变换做离散化,在很多控制器里都能得到较稳定的结果。A_d和B_d离散之后,就可以在仿真主循环里不断计算反馈转角:
delta_fb = -K @ error_state3.3 最小闭环仿真框架
仿真的主流程可以参考下面这个简化框架:
v_x = 20.0 # 纵向车速 state = np.zeros(4) # [ey, e_psi, ey_dot, e_psi_dot] for t in np.arange(0, 10, dt): # 从轨迹模块获取参考点的曲率和航向速度 kappa_ref = get_curvature(t) psi_dot_ref = v_x * kappa_ref # 计算误差状态(此处依赖具体模型定义) A_val, B_val = build_error_model(v_x, vehicle_params) K = compute_lqr_gain(A_val, B_val, Q, R, dt) delta = -K @ state # 用delta仿真车辆状态更新 state = vehicle_update(state, delta, ref_info, dt)这个框架里最核心的部分是build_error_model函数,你需要根据自己的车辆参数和参考坐标系定义来填充A与B的值。LQR的意义在于,一旦A、B、Q、R定了,K的求解是完全确定的,不需要人工反复试PID三参数,这是它最好用的一点。
4. 参数调优实战:Q/R矩阵与稳态误差的坑
4.1 一套能跑通的初始参数与其效果
我在初版仿真里用过的参数大概是:m = 1450 kg,Iz = 2100 kg·m²,lf = 1.2 m,lr = 1.4 m,Cf = Cr = 60000 N/rad,车速分别测试15、20、25、30 m/s。Q取对角阵diag([20, 100, 10, 10]),R取1,采样周期10ms。
在这个配置下,直线段收敛很快,横向误差从0.5m初始偏差恢复到0.05m以内大概需要2秒不到,瞬态超调量不大。但在标准双移线测试里发现一个问题:弯道稳态段横向误差始终存在一个非零残差,大约5~8cm,无法通过继续调大Q[ey]彻底消除。
这个现象在LQR轨迹跟踪里非常经典。原因在于:LQR本质是一个调节器,它设计的目标是让状态误差稳定在零,但是如果参考轨迹本身在持续变化(比如曲率不为零),稳态时需要的并不只是反馈项,还需要一个前馈控制量来“抵消”曲率带来的强制扰动。如果只有反馈没有前馈,系统稳态时就会靠一个误差来维持平衡,这就是稳态残差的来源。
4.2 前馈补偿:LQR想做好轨迹跟踪必须做的加法
工程上通常把前轮转角写成两部分:
δ = δ_fb + δ_ff其中δ_fb = -K X是LQR反馈项,δ_ff是前馈项。前馈项的核心思想是:当车辆以车速v过曲率为κ的弯道时,车辆本身需要一个基本的前轮转角才能保持转弯。这个基准转角可以根据稳态转向关系来计算:
δ_ff ≈ (L + K_ug * v²) * κ式子里的K_ug是车辆的不足转向梯度,由质量、轴距和各轴侧偏刚度决定。做前馈补偿时不需要百分百精确,即使只补偿80%的基准转角,也能把稳态残差从厘米级压到毫米级,剩下的由反馈项自动修正。
加前馈之后我又做了一次双移线测试,稳态横向误差从之前的5~8cm降到1cm以内。这里有个小经验:不要试图靠反馈调节器一个劲儿地增大Q[ey]来消除稳态误差,那只会让系统更“僵硬”,弯道里的瞬态摆动反而变大。正确做法是前馈处理低频的基准控制量,反馈只负责误差修正。
4.3 边界检验:轮胎线性区间、执行器饱和和控制延迟
LQR的一个潜在风险是它依赖线性模型,如果车辆进入大侧偏角区,轮胎侧偏力不再随转角线性增长,实际系统的控制效果会和模型预测显著偏离。我测试高附着路面、车速25m/s以下时,LQR表现非常好;但遇到低附着路面模拟(比如路面摩擦系数降到0.4以下),同样的增益K就会显得“自信过头”,前轮转角输出偏大,车辆横向响应却跟不上,表现是轨迹振荡。
处理办法有三种,由简单到复杂:
- 降低Q矩阵对角元素,让控制器更“温吞”,减少控制量输出;
- 根据车速实时调整K,因为车速变化会影响A矩阵中的速度项,增益需要做调度;
- 改为基于线性时变模型的LQR,在每个控制周期根据当前车速重新线性化和求解黎卡提方程。
另外要留意执行器饱和。实车方向盘或转向执行器有最大转角限制,仿真里如果K过大,输出转角会直接顶到饱和边界。饱和状态下LQR的线性假设彻底失效,表现就是振荡和超调。一个有效的小技巧是在目标函数R里留一点余量,让最优解不至于总是贴着极限走。
4.4 调参的顺序感
很多新手拿到Q/R矩阵就直接乱改,这种耗时又无效。我自己习惯的调参顺序是:先用比较大的Q[ey]确定横向误差的快速收敛能力,然后提高Q[e_psi]控制瞬态超调,再调R控制高频抖动的程度,最后回到Q矩阵做精细修正。每改一个参数,跑一次固定的双移线工况,对比横向误差、横摆角误差和控制量曲线的变化,而不是只在人工看视频里感觉“稳不稳”。
5. 从仿真到落地的几个工程细节
5.1 离散化方式和采样周期的选择
我在仿真里用了双线性变换做离散化,但在真实控制器里,更标准的选择是零阶保持离散化,也就是假设控制量在一个周期内保持不变,然后精确求矩阵指数。采样周期通常选10ms~20ms,太低会导致离散化误差明显,太高则会限制控制带宽,车感“发肉”。
还有一个容易被忽视的坑:分段写A矩阵时车速v在变化,每个周期要重新计算A_d和B_d。如果图省事用固定v来算K,实际车速变了之后系统增益不匹配,可能出现低速还好、高速发散的情况。
5.2 与规划模块的数据衔接
LQR控制器本身只是横向控制的一环,它需要从规划模块拿到三样东西:参考轨迹点的位置、参考切线方向和参考曲率。轨迹跟踪控制的关键预判能力,其实很大程度取决于“误差状态是在哪个坐标系下定义的”。
我使用的办法是把误差统一换算到车辆坐标系下的最近参考点,即把参考轨迹按当前位置做坐标变换,得到纵向距离s、横向误差ey和横摆角误差e_psi。这种表达下,横向误差和横摆角误差的符号不容易混乱,LQR的状态也清晰。需要注意的是,搜索“最近参考点”这个操作需要一定的计算开销,如果轨迹点很多,建议做预处理,避免每条控制周期都全量搜索。
5.3 一点来自项目的实用体会
我实际调试LQR时最破防的一个时刻是:所有参数看起来都合理,增益矩阵也和书本示例相近,但仿真结果里系统就是高频抖动。排查了很久,最后发现是误差状态更新频率和控制频率不一致,状态量在一个周期里重复叠加了更新,导致等效增益翻倍。做集成时真的建议自己写一个状态更新的单测,单独验证误差递推逻辑,再接入控制循环。
如果你是从纯跟踪或者PID方案迁移到LQR,我还有一个建议:保留原有的PID控制器作为后备,新控制器先在低车速、简单轨迹的工况下并行运行,确认无误后再提升车速和轨迹复杂度。控制系统落地最重要的是渐进验证,不要在第一天就挑战极限工况。
关于轨迹跟踪,LQR并不是终点,MPC、自适应控制等在工程上也有各自的用武之地。但LQR提供了一个极好的起点,它让你在可控的成本内理解“基于模型的控制”到底是什么感觉:建模、误差定义、目标函数、求解、前馈补偿、边界检验,这一整套流程吃透了,后面再学更复杂的控制方法会顺畅很多。