我第一次在自动驾驶项目中同时接触MPC和LQR时,最困惑的就是它们工作时域的区别。这就像比较两种不同的旅行规划方式:LQR像是制定完整的行程表,出发前就把每天去哪、住哪都安排妥当;而MPC更像是边走边规划,每到一个地方再根据实际情况调整后续路线。
具体到技术层面,LQR的无限时域特性让它能给出全局最优解。我在车辆横向控制测试中发现,当系统模型足够准确时,LQR确实能提供非常平滑的控制曲线。但问题在于现实世界充满不确定性——有一次测试中,突然出现的道路施工就让预先计算的轨迹完全失效。这时MPC的滚动优化优势就显现出来了,它每100ms重新计算一次轨迹,就像老司机不断微调方向盘,实测下来对突发障碍物的响应速度比LQR快40%。
不过MPC的有限时域也带来新问题。在高速过弯场景下,我发现如果预测时域设置过短(比如小于2秒),车辆会出现"短视"行为——为了眼前的最优反而偏离了全局最优路径。经过多次调试,最终确定3秒的预测时域配合1秒的控制时域,能在计算量和控制效果间取得最佳平衡。
去年给物流小车做轨迹跟踪时,我深刻体会到算力约束对算法选择的影响。原本计划用MPC实现精准控制,但在树莓派上跑起来才发现——预测时域设为20步时,单次求解就要300ms,完全达不到实时性要求。
后来改用LQR后,计算时间直接降到5ms以内。这里有个实用技巧:对于状态矩阵A维度不大的系统(比如我们用的4状态模型),可以预先计算好Riccati方程的解,运行时直接查表调用。实测下来,这种方案在ARM Cortex-A72处理器上能稳定跑100Hz的控制频率。
但MPC在高端硬件上确实能发挥威力。用NVIDIA Xavier测试时,配合acados求解器,50步预测时域的MPC也能跑到50Hz。这里分享一个参数调优经验:QP求解器的迭代次数不要超过10次,否则实时性会急剧下降。具体配置可以参考这个模板:
python复制solver_options = {
'qp_iter_max': 10,
'hessian_approx': 'GAUSS_NEWTON',
'integrator_type': 'ERK'
}
在自动泊车项目里,我遇到了典型的非线性问题——低速工况下轮胎动力学呈现强非线性特性。最初尝试用LQR控制时,车辆在靠近目标车位时会出现"画圈"现象。分析日志发现,这是因为线性化模型在大转向角时误差超过15%。
转用MPC后,我采用了两种非线性处理方法:一是基于车辆状态的动态线性化,每次求解前更新雅可比矩阵;二是直接使用非线性MPC(NMPC)。实测数据显示,前者在RTK定位条件下能将控制误差控制在5cm内,而后者虽然精度更高(3cm内),但计算耗时增加了3倍。
这里有个坑要注意:非线性优化可能陷入局部最优。有次测试中车辆在直角弯处突然刹停,就是因为求解器卡在了局部最优解。后来通过增加初始猜测的随机扰动解决了这个问题,代码实现如下:
cpp复制// 添加高斯噪声避免局部最优
for(int i=0; i<horizon; i++){
x_guess[i] += 0.05 * normal_distribution(engine);
}
现在主流的自动驾驶架构其实都在混用MPC和LQR。以我参与过的NOA项目为例,横向控制用LQR做基础稳定控制,纵向用MPC处理跟车场景,这种组合既保证了实时性又兼顾了灵活性。
具体实现上有几个关键点:
在轨迹跟踪场景中,我开发了一套动态切换策略:当预测误差小于0.1m时用LQR,大于0.3m切到MPC。实测表明这种方案能降低平均30%的CPU占用,特别适合算力受限的域控制器。
调参是控制算法工程师的必修课。经过多个项目积累,我总结出这些经验值:
有个可视化调试工具特别推荐:MATLAB的Control System Tuner。它能实时显示闭环响应曲线,我通常先用它做离线调参,再把参数移植到实车。记得有次调横向控制时,通过观察Bode图发现相位裕度不足,调整Q矩阵后振荡问题立刻解决。
根据不同的应用场景,硬件选择差异很大:
在内存配置上,MPC需要特别注意堆栈分配。曾经遇到过一个内存越界bug,是因为预测时域设为30步时,状态变量数组超出了预设范围。现在我的工程模板里都会加上这段防护代码:
c复制#define MAX_HORIZON 50
typedef struct {
double x[MAX_HORIZON];
double u[MAX_HORIZON];
} MPCBuffer;
最近在关注结合机器学习的方法,比如用神经网络预测MPC的初始解。测试数据显示,这种方法能将迭代次数从平均8次降到3次。另一个趋势是分布式MPC,特别适合卡车队列这种多智能体场景。
不过要说最实用的创新,我觉得是事件触发式MPC。传统的时间触发会浪费计算资源,而通过设置状态变化阈值来触发重计算,在实际路测中能减少60%的无意义求解。实现起来也很简单:
python复制if np.linalg.norm(x_actual - x_pred) > threshold:
mpc.solve()
这些年在不同项目间切换,越来越觉得没有最好的控制器,只有最合适的控制器。就像工具箱里的扳手和锤子,关键是要清楚什么时候该用哪个。最近做的农业机器人项目,甚至在同一个系统里混用了PID、LQR和MPC——执行机构用PID,姿态控制用LQR,路径跟踪用MPC,这种组合反而取得了最佳效果。