1. 为什么我们需要模型预测控制(MPC)来做轨迹跟踪?
第一次接触车辆轨迹跟踪问题时,我尝试用传统的PID控制器。在仿真环境下跑直线轨迹时效果还不错,但遇到急转弯或者S型曲线时,车辆就开始"画龙"了。这就是经典控制理论的局限性——它只能根据当前误差做出反应,无法预见未来的轨迹变化。
模型预测控制(MPC)就像一个有经验的赛车手,它不会只看眼前的路况,而是会:
- 预判未来几秒的轨迹走向
- 考虑车辆动力学约束(比如最大转向角)
- 在多个可能的控制方案中选择最优解
举个生活中的例子:PID控制就像蒙着眼睛走路,靠脚碰到障碍物才知道要转弯;而MPC则是睁着眼睛走路,提前看到障碍就规划绕行路线。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 搭建车辆动力学模型:从自行车模型说起
2.1 为什么选择自行车模型?
在车辆控制领域,最常用的简化模型是自行车模型(Bicycle Model)。它把四轮车辆简化为前后两轮,就像骑自行车一样。这个模型虽然简单,但抓住了车辆运动的本质特性:
matlab复制% 自行车模型状态方程示例
function dxdt = bicycleModel(t, x, u)
% 状态变量: x(1)=X坐标, x(2)=Y坐标, x(3)=航向角, x(4)=速度
% 控制输入: u(1)=前轮转角, u(2)=加速度
L = 2.9; % 轴距(m)
dxdt = zeros(4,1);
dxdt(1) = x(4)*cos(x(3)); % X方向速度
dxdt(2) = x(4)*sin(x(3)); % Y方向速度
dxdt(3) = x(4)*tan(u(1))/L; % 航向角变化率
dxdt(4) = u(2); % 加速度
end
这个模型忽略了一些次要因素(如轮胎侧偏刚度),但计算量小、实时性好,特别适合作为MPC的预测模型。我在实际项目中测试过,在车速低于60km/h时,这个模型的预测精度完全够用。
2.2 模型参数的实测标定技巧
模型中的关键参数是轴距L,这个值不能简单照搬车辆说明书。我推荐的做法是:
- 让车辆以恒定速度v行驶
- 固定方向盘转角δ,记录车辆画出的圆弧半径R
- 根据几何关系:L = R×tan(δ)
- 多次测量取平均值
实测中发现:低速时测得的值会比说明书小5%-10%,这是因为轮胎存在弹性变形。建议在不同速度下分段标定参数。
3. MPC控制器的核心设计流程
3.1 预测时域与控制时域的选择
这是MPC调参的第一个难点。通过大量实验,我总结出以下经验法则:
| 车速 (km/h) | 预测时域 (s) | 控制时域 (步数) |
|---|---|---|
| <30 | 1.5-2.0 | 10-15 |
| 30-60 | 2.0-3.0 | 15-20 |
| >60 | 3.0-5.0 | 20-30 |
原理是:车速越高,需要预判的时间越长。但时域过长会导致:
- 计算量指数级增长
- 预测精度下降(模型误差累积)
3.2 代价函数的艺术设计
好的代价函数应该像老司机开车一样平衡多个目标:
matlab复制function J = costFunction(X_ref, U_ref, X_pred, U_pred)
% 轨迹跟踪误差
pos_error = sum((X_pred(1:2,:) - X_ref(1:2,:)).^2, 'all');
% 控制量变化率
steer_rate = diff(U_pred(1,:)).^2;
accel_rate = diff(U_pred(2,:)).^2;
% 控制量幅值惩罚
steer_penalty = sum(U_pred(1,:).^2);
% 权重系数需要反复调试
w1 = 1.0; w2 = 0.1; w3 = 0.1; w4 = 0.01;
J = w1*pos_error + w2*sum(steer_rate) + w3*sum(accel_rate) + w4*steer_penalty;
end
调试时常见误区:
- 只关注位置误差,导致控制量剧烈抖动
- 转向权重过大,车辆响应迟钝
- 忽略变化率项,方向盘像新手一样忽左忽右
4. Matlab实现中的性能优化技巧
4.1 用CasADi替代fmincon
原生Matlab的fmincon求解器速度较慢,我强烈推荐使用CasADi工具包:
matlab复制import casadi.*
opti = casadi.Opti();
% 定义优化变量
X = opti.variable(4, N+1); % 状态序列
U = opti.variable(2, N); % 控制序列
% 构建约束条件
for k = 1:N
opti.subject_to(X(:,k+1) == RK4(@bicycleModel, X(:,k), U(:,k), dt));
end
opti.subject_to(-0.5 <= U(1,:) <= 0.5); % 转向角约束
opti.subject_to(-2 <= U(2,:) <= 2); % 加速度约束
% 求解
p_opts = struct('expand', true);
s_opts = struct('max_iter', 100);
opti.solver('ipopt', p_opts, s_opts);
sol = opti.solve();
实测对比:相同问题上CasADi比fmincon快3-5倍,特别适合实时控制。
4.2 热启动(Hot Start)技巧
MPC是滚动优化的,可以利用上一时刻的解来加速当前求解:
matlab复制% 第一次求解
sol = opti.solve();
U_prev = sol.value(U);
% 下一次优化前
opti.set_initial(U, [U_prev(:,2:end), U_prev(:,end)]);
这就像开车时不断微调方向盘,而不是每次都从头开始打方向。我的测试表明,热启动可以减少30%-50%的迭代次数。
5. 实际测试中的典型问题与解决方案
5.1 延迟补偿:控制器跟不上实际车辆
在真车测试时,我发现控制器输出总是慢半拍。这是因为:
- 传感器通信延迟(约50-100ms)
- 执行器响应延迟(如EPS转向系统)
- 求解计算耗时
解决方法是在预测模型中加入延迟补偿:
matlab复制% 在状态方程中增加延迟状态
dxdt(5) = u(1); % 实际转向角
dxdt(6) = u(2); % 实际加速度
% 使用一阶惯性环节模拟执行器延迟
tau_steer = 0.1; % 转向时间常数
tau_accel = 0.2; % 加速时间常数
real_steer = (x(5) - u(1))/tau_steer;
real_accel = (x(6) - u(2))/tau_accel;
5.2 曲率突变处的轨迹跟踪
当参考轨迹的曲率突然变化(比如直角弯),车辆容易发生超调。我的改进方案:
- 在代价函数中增加曲率变化率惩罚项
- 对参考轨迹进行预处理,用三次样条插值平滑
- 根据曲率动态调整预测时域(弯道处加长)
matlab复制% 曲率计算
function k = curvature(x_ref)
dx = gradient(x_ref(1,:));
ddx = gradient(dx);
dy = gradient(x_ref(2,:));
ddy = gradient(dy);
k = (dx.*ddy - dy.*ddx) ./ (dx.^2 + dy.^2).^1.5;
end
6. 进阶:考虑轮胎非线性特性的MPC
当车辆接近物理极限(如高速过弯)时,自行车模型的线性假设不再成立。这时需要引入更复杂的轮胎模型:
matlab复制% Pacejka轮胎模型
function Fy = pacejkaModel(alpha, Fz)
% 参数示例(需实测标定)
B = 10; C = 1.5; D = 1.0; E = 0.5;
Fy = Fz * D * sin(C * atan(B*(1-E)*alpha + E*atan(B*alpha)));
end
实现步骤:
- 计算每个轮胎的侧偏角α
- 通过Pacejka公式计算侧向力Fy
- 将Fy引入车辆动力学方程
- 在线性MPC框架下用迭代法处理非线性
这个方案计算量较大,建议:
- 只在高速场景启用
- 使用C代码生成加速计算
- 采用简化版的Pacejka模型
我在某自动驾驶项目中实测发现,加入轮胎模型后,80km/h过弯的横向误差减少了42%。
