控制算法概览:自动驾驶为什么需要PID和MPC?

大家好,我是你们的老朋友,一个在自动驾驶控制领域摸爬滚打多年的工程师。今天咱们开始聊点硬核的——PID和MPC。

你可能会问,自动驾驶这么高大上的东西,为啥还要用PID这种“老古董”?MPC又是什么神仙算法?别急,咱们慢慢聊。

先从一次路测说起

我记得几年前,我参与一个L4级项目的路测。那天下着小雨,路面有点湿滑。车辆在跟车时,突然前车急刹。我们的车呢?先是猛地一顿,然后开始剧烈抖动,最后才慢慢停下来。坐在后排的产品经理脸都绿了。

后来排查原因,发现是纯PID控制器的参数没调好。在湿滑路面上,积分项过度补偿,导致车辆反复“点头”。那次之后,我深刻理解了一个道理:没有万能的控制器,只有合适的算法组合

自动驾驶为什么需要控制算法?

说白了,自动驾驶就是让车自己走。但车不是人,它不知道什么时候该加速、什么时候该转弯。控制算法就是给车装上一套“神经系统”。

这套系统要解决三个核心问题:

  • 跟踪路径:让车沿着规划好的路线走,别跑偏
  • 保持稳定:遇到颠簸、侧风、湿滑路面,车不能失控
  • 响应指令:说加速就加速,说刹车就刹车,不能有延迟

你想想看,如果控制算法不行,再好的感知、再完美的规划都是白搭。车都开不稳,还谈什么自动驾驶?

PID:简单粗暴的“老司机”

PID算法,全称比例-积分-微分控制。它就像一个经验丰富的老司机,凭感觉开车。

核心思想:看当前偏差,算过去偏差,预测未来偏差。

  • P(比例):偏差越大,修正越猛。比如车偏右了,立刻向左打方向。
  • I(积分):消除长期累积的误差。比如一直有侧风,车总往右偏,积分项会慢慢把车拉回来。
  • D(微分):抑制震荡。比如车开始左右摇摆,微分项会提前“踩刹车”。

适用场景

  • 直线巡航、定速巡航
  • 简单的车道保持(弯道不太急的情况)
  • 执行器底层控制(比如油门、刹车、转向的精确调节)

我在项目中遇到过一件事:用PID做纵向控制(油门刹车),效果出奇的好。但一到横向控制(转向),尤其是高速过弯时,PID就开始“手忙脚乱”。为什么?因为PID没有“预知未来”的能力,它只能看到当前的偏差,等它反应过来,车已经偏出去了。

个人经验:PID调参有个口诀——“先P后I再D,P大响应快,I大消除静差,D大抑制震荡”。但别死记硬背,实际调参时,我建议先用仿真环境跑一遍,看看响应曲线再动手。

MPC:运筹帷幄的“军师”

MPC,模型预测控制。它不像PID那样“走一步看一步”,而是“走一步,看三步”。

核心思想:基于车辆模型,预测未来一段时间的状态,然后求解一个优化问题,找到最优的控制序列。

说白了,MPC会问自己:“如果我接下来3秒内这样打方向、这样踩油门,车会走到哪里?会不会撞墙?会不会偏离车道?”然后它选一个最好的方案。

适用场景

  • 高速变道、紧急避障
  • 复杂弯道、匝道行驶
  • 多目标约束(比如既要跟车距离近,又要舒适,还不能超速)

我曾经用MPC做过一个泊车项目。车辆需要在狭小的空间内,前后移动多次才能停进去。PID根本搞不定,因为每次倒车都会产生新的偏差。但MPC可以提前规划好整个路径,一次性执行到位。嗯,这就是“预知未来”的好处。

避坑指南:我曾经在MPC的权重矩阵上栽过跟头。如果你把“舒适性”的权重设得太高,车会变得“畏手畏脚”,该加速时不加速,该转向时不转向。反过来,如果“跟踪精度”权重太高,车会变得很“暴躁”,乘客晕车是小事,搞不好还会失控。调MPC的权重,比调PID参数难一个数量级。

PID vs MPC:怎么选?

你可能会问,那我到底该用PID还是MPC?

我的建议是:看场景,看资源

对比维度 PID MPC
计算资源 极低,单片机都能跑 较高,需要高性能处理器
调参难度 中等,3个参数 较高,权重矩阵+预测时域
处理约束 困难,需要额外逻辑 天生支持,直接建模
预测能力 有,可提前规划
鲁棒性 较好,对模型误差不敏感 一般,依赖模型精度

我个人习惯是:能用PID解决的,绝不用MPC。为什么?因为PID简单、稳定、好调试。但遇到复杂场景,比如高速变道、紧急避障,我会毫不犹豫地切换到MPC。

你想想看,如果只是定速巡航,用MPC就是杀鸡用牛刀。但如果要做L4级别的自动驾驶,没有MPC几乎不可能。

一个简单的代码示例

这里给一个PID的伪代码,帮你理解它的本质:

// 位置式PID
float pid_control(float target, float current) {
    float error = target - current;
    
    // P项
    float p_out = Kp * error;
    
    // I项(累积误差)
    integral += error * dt;
    float i_out = Ki * integral;
    
    // D项(误差变化率)
    float derivative = (error - last_error) / dt;
    float d_out = Kd * derivative;
    
    last_error = error;
    
    return p_out + i_out + d_out;
}

你看,就这么几行代码。但别小看它,调好了能跑出花来。

MPC的代码就复杂多了,涉及到矩阵运算和优化求解器。这里不展开,后面章节会详细讲。

小结一下

PID和MPC,一个像“老司机”,凭经验开车;一个像“军师”,运筹帷幄。两者没有绝对的好坏,只有合不合适。

在实际工程中,我见过很多团队把PID和MPC结合起来用:底层执行器用PID,上层决策用MPC。这样既保证了响应速度,又兼顾了全局优化。

好了,第一章就聊到这里。下一章,咱们深入PID的数学原理,看看这三个参数到底是怎么影响系统行为的。到时候我会分享一些调参的“野路子”,保证让你少走弯路。

记住:控制算法不是纸上谈兵,多动手、多试错,才能真正掌握。