从0到1带你打电赛·小车电控篇(六):PID 入门——从「把速度调稳」开始,一次搞懂 P、I、D

到这一篇,我们终于要碰电控里”听起来最玄、其实最值钱”的东西了——PID。

前面几篇,我们给小车装好了电机、电源(电机驱动与电源地基那篇),又装好了灰度、编码器、IMU 这些”感官”(感知那篇)。现在小车能动、也能”知道自己现在多快、偏了多少”,可它还差最后一口气:不会自己根据偏差去纠正。这一步,就是 PID 干的活。

很多人对 PID 有两种相反的误解。一种把它当高深算法,看见公式里的积分微分就头大,绕道走;另一种觉得”不就三个数嘛,乱试呗”,结果调了一下午,车还在地上画龙。这篇我们专治这两种病:先用大白话把 P、I、D 三个字母到底在干嘛讲透,建立直觉;再讲清两种最常见的写法(位置式和增量式)到底差在哪、怎么选;最后手把手带你做”人生第一次调参”——只调一个电机的速度环,从只有 P 开始,一步步加到能稳稳跟住目标速度。

📝 本篇只打地基

这一篇我们只做单环(一个电机、一个速度环),目的是建立直觉、跑通流程。真正比赛用的”方向环 + 速度环串级双环”,还有那一堆”不做就翻车”的工程补丁(积分限幅、抗饱和、微分滤波……),留到 PID 进阶那一篇专门讲。这里别贪多,先把一个环调明白,比啥都强。

先搞懂一件事:什么是”负反馈”

在拆 P、I、D 之前,得先有个总画面。PID 本质上是一套负反馈的纠错逻辑,一句话概括:

拿”你想要的目标”减去”传感器测到的实际值”,得到一个偏差;再根据这个偏差算出一个控制量去纠正,让实际值往目标靠。偏差越大纠得越狠,偏差归零就收手。

它一直在转圈:测量 → 算偏差 → 输出 → 再测量……画成图就是这样一个闭环:

电赛小车电控 · 示意图

给它起几个固定名字,后面一直用:

  • 目标值(setpoint):你希望它达到的值。比如”电机转速 = 100″。
  • 实际值(反馈):传感器测到的当前值。比如编码器测出来现在只有 60。
  • 偏差(error):$e = \text{目标} – \text{实际}$。上面这例子 $e = 100 – 60 = 40$。
  • 控制量(output):算出来要给执行机构的指令。对电机来说,就是 PWM 占空比。

💡 一个贯穿全篇的比喻:开车去停车线

你要把车停在前面一条线上。目标 = 那条线的位置,实际 = 你现在的位置,偏差 = 还差多远,你脚下的油门/刹车 = 控制量。离得远就猛踩油门,快到了松油门、点点刹车——你大脑里其实一直在跑一个”负反馈”。PID,就是把这套人脑里的本能,写成了三行可以照抄的公式。

而 P、I、D,就是从三个不同的时间视角去看同一个偏差:P 看现在、I 看过去、D 看未来。下面一个个拆。

P:看现在——偏差多大就纠多狠(管”快”)

P(Proportional,比例)是最直接的一项:当前偏差有多大,就按比例给多大的纠正力度。公式就一行:

$$u_P = K_p \cdot e$$

$K_p$ 是你要调的比例系数。偏差大、纠正猛;偏差小、纠正轻;偏差归零,这一项也归零。

🧩 开车的 P

离停车线还有 100 米,你油门踩得很深;还剩 5 米,你只轻轻给一点。”差得越多、踩得越狠”,这就是 P。$K_p$ 就是你的”急脾气程度”——同样差 100 米,急脾气的人一脚油门到底,慢性子的人才慢慢加速。

P 决定了系统反应有多快、有多”硬”。这就是为什么常说 P 管”快”

⚠️ P 不是越大越好

$K_p$ 太小:车反应迟钝、半天到不了目标,慢吞吞。 $K_p$ 太大:纠过头。还没到目标就冲过去了,冲过去又被拉回来,来回振荡,甚至越晃越大直接发散(车失控冲出去)。 而且,光靠 P 往往会留一条尾巴——稳态误差:车最后总是差那么一丢丢,停不到线上。原因是偏差一旦变小,$K_p \cdot e$ 这个力度也跟着变小,小到刚好抵消阻力(摩擦、坡度、风),系统就”卡”在那个差一点的地方不动了。这条尾巴,得靠下面的 I 来收拾。

I:看过去——把欠的账一点点补上(管”准”)

I(Integral,积分)专治 P 留下的那个”差一丢丢”的尾巴。它的逻辑是:把过去每一拍的残余偏差都累加起来,攒到一定程度,逼着系统把这点欠账补上

$$u_I = K_i \cdot \sum e$$

只要还有偏差没消掉,这个累加和 $\sum e$ 就一直在涨,对应的纠正力度也越来越大,直到偏差真正归零它才停止增长。所以 I 是专门消除稳态误差的,决定了系统最终能不能精确命中目标——这就是 I 管”准”

🧩 洗澡调水温的 I

你想要 40 度,水管有热损耗,光靠 P(凉了就开大热水)总是卡在 38 度上不去。这时你心里那个”老差 2 度”的不爽会一点点累积,最后促使你”再多拧一点热水”,把这 2 度补回来。这个”把长期的小遗憾累加起来、最后一并补上”的劲儿,就是 I。

⚠️ I 调大的后果

$K_i$ 太小:欠账补得太慢,稳态误差迟迟消不掉。 $K_i$ 太大:补过头!因为它会”闷头攒”——等偏差终于归零时,前面攒下的那一大坨累加量一时撒不掉,结果冲过头、超调变大、来回振荡,整个系统反而变慢、变得不稳。 还有一个更隐蔽的坑叫积分饱和(windup):如果偏差长期消不掉(比如车被卡住了、或者 PWM 已经顶到 100% 还不够),这个累加和会一路涨到天上去;等情况好转该收手时,这一肚子”攒下的火气”撒不掉,猛地冲过头。这个坑的解法(积分限幅、抗饱和)属于工程补丁,放到 PID 进阶那一篇细讲。本篇你只要记住一句:”I 不能贪大,攒过头会反噬“。

D:看未来——看趋势提前刹车(管”稳”)

D(Derivative,微分)是三项里最”聪明”的一项。它不看偏差本身有多大,而看偏差正在以多快的速度变化(这一拍的偏差减上一拍的偏差):

$$u_D = K_d \cdot (e_k – e_{k-1})$$

如果偏差正在飞快缩小,说明你正猛冲向目标,再不收手就要冲过头了——D 这时给一个反向的力,相当于提前点刹车,专门用来抑制超调、增加阻尼、让系统更稳。所以 D 管”稳”

🧩 骑自行车的 D

老司机的口诀是”车身往哪倒,龙头往哪拐“。注意——你不是等真摔倒了才去救,而是看到车身正在往左倒(偏差在往一个方向变化)就提前往左打方向。这种”盯着趋势、提前动手”的预判,就是 D 的精髓。

🔥 D 的最大副作用:放大噪声

$K_d$ 太大,会出大问题。因为 D 是对”变化量”做反应,而传感器的噪声(编码器测速的毛刺、陀螺仪角度的抖动)本身就是高频的剧烈变化——D 会把这些噪声当成真实趋势,放大成控制量的剧烈抖动,电机/舵机跟着发抖,曲线长满毛刺,越调越糟。 所以 D 一定要从 0 缓慢往上加,一旦看到曲线开始长毛刺、电机发抖,立刻停手并回调 10%~20%。如果噪声实在大,正确做法是给 D 项加个低通滤波(叫”不完全微分”),而不是一味硬压小 $K_d$——这个技巧同样留到进阶篇。 另外记牢一句:D 治不了稳态误差。偏差不变时($e_k = e_{k-1}$),D 输出为 0,它对”卡在那不动的尾巴”完全无能为力,那是 I 的活。

三个一起上:完整的 PID

把三项加起来,就是完整的 PID:

$$u(k) = K_p \cdot e(k) + K_i \cdot \sum e(i) + K_d \cdot [e(k) – e(k-1)]$$

用三个性格来记,特别好用:

看的是 管什么 性格比喻 调大了会
P 现在的偏差 (响应速度、刚度) 急性子:差多少补多少,反应快但容易用力过猛 振荡、发散
I 过去的累计偏差 (消稳态误差) 记仇的人:每笔小账都记着,死磕到”一丝不差” 超调、积分饱和、变慢
D 偏差变化的趋势 (抑制超调、阻尼) 预言家:看你冲太快就提前喊停 放大噪声、电机发抖

❗ 务必记牢:"P 快、I 准、D 稳"

网上有些资料把它写成”P 管稳、D 管快”——这是说反了。权威控制教材(密歇根 CTMS、Caltech AM08 等)的结论是统一的:增大 $K_p$ 会加快响应(减小上升时间),所以 P 管”快”;$K_d$ 增加阻尼、抑制超调、改善稳定性,所以 D 管”稳”。只有 I 管”准” 这一条三方都没争议。照 P 快 / I 准 / D 稳 来记,别被对调的版本带偏。

两种写法:位置式 vs 增量式

上面那个公式,是位置式的写法。工程上还有一种增量式,两者长得不一样、用途也不一样,是新手最容易混的地方。务必搞清楚,否则照抄别人代码会出大问题。

位置式:直接算出”绝对的”控制量

就是上面那条完整公式,它算出来的 $u(k)$ 是一个绝对值——比如”舵机应该转到 37 度”。

$$u(k) = K_p \cdot e(k) + K_i \sum_{i} e(i) + K_d \cdot [e(k) – e(k-1)]$$

特点: – 输出直接对应执行机构的绝对位置/角度,符合”我要它在哪”这种直觉。 – 含一个全历史误差累加 $\sum e$,所以容易积分饱和,必须配积分限幅。 – 一旦某次算错、或者程序重启,因为输出是绝对值,执行机构会直接跳变到那个错误位置,有点危险。

增量式:只算”在上次基础上加减多少”

增量式算的不是绝对值,而是这一拍相对上一拍要变化多少(增量 $\Delta u$),最后累加回去:

$$\Delta u(k) = K_p[e(k)-e(k-1)] + K_i \cdot e(k) + K_d[e(k)-2e(k-1)+e(k-2)]$$

$$u(k) = u(k-1) + \Delta u(k)$$

它只跟最近 3 拍的误差有关,不显式累加全部历史。特点: – 不显式累加 $\sum e$,所以天然规避了那种”积分项无限膨胀”的经典饱和。 – 某次算错只影响一个增量,误动作影响小、更安全,也便于”手动/自动”无扰切换。 – 缺点:靠累加 $\Delta u$ 间接实现积分,可能残留一点稳态误差;而且 3 拍差分对噪声更敏感。

📝 别误会成"绝对不会饱和"

严格说,增量式只是不易发生显式的积分饱和,不是”绝对免疫”。当 $u(k)=u(k-1)+\Delta u$ 这个输出本身被限幅(比如 PWM 顶到 100%)时,照样会出现类似饱和的退出延迟,真较真起来工程上还是要做抗饱和处理。所以准确的说法是”天然规避显式积分饱和“,而不是”天生绝不饱和”。

怎么选?

记住这个比喻就够了:

💡 油门 vs 方向盘

位置式像报”油门踩到第几格”的绝对值——你要的就是一个绝对位置(舵机要转到某个绝对角度),适合它;但算错一次或重启就直接跳到错误档位(突变危险),还得记一路的总账(容易饱和)。 增量式像每次只说”油门再多/再少一点点”——说错一次只差一点点(影响小),不用记总账(不易饱和),对电机 PWM 做”增/减”最自然。

所以工程惯例是: – 电机速度环 → 用增量式(通常是 PI,测速噪声大一般不加 D),对 PWM 做 pwm += Δu 最顺手; – 舵机方向环 / 角度环 → 用位置式(通常是 PD,转向加 I 会延迟、弯道画龙,一般不加 I)。

这个”速度环增量式、方向环位置式”的搭配,是几乎所有智能车队的标配。本篇先把增量式速度环跑通;方向环长啥样、两者怎么串成两级,是 PID 进阶那一篇的主题。

⚠️ 两种写法的系数不能直接照抄

位置式里乘 $\sum e$ 的 $K_i$,和增量式里乘 $e(k)$ 的 $K_i$,量纲和数值完全不同(增量式里乘 $e(k)$ 的那个系数其实承担了”积分”的角色)。网上很多博客把两种式子的系数混着贴,你照抄过来会差出十万八千里。换写法 = 重新整定

上代码:两种 PID 的最小实现

光看公式没感觉,看代码就清楚了。先是位置式(最容易懂的入门写法,已经带上积分限幅和输出限幅这两道保险——为什么要加、怎么加得更好,进阶篇讲,这里先照抄):

// 位置式 PID:输出当作绝对量(如舵机角度)
float Position_PID(float target, float measure){
    static float integral = 0, last_err = 0;
    float err = target - measure;          // 偏差 = 目标 - 实际

    integral += err;                       // I:累加历史偏差
    // 积分限幅(防止积分饱和,进阶篇细讲)
    if(integral >  INT_MAX) integral =  INT_MAX;
    if(integral < -INT_MAX) integral = -INT_MAX;

    float out = Kp*err                     // P:看现在
              + Ki*integral                // I:看过去
              + Kd*(err - last_err);       // D:看未来(偏差变化)
    last_err = err;                        // 记住这次偏差,下次算 D 用

    // 输出限幅(必做!否则可能烧电机/舵机)
    if(out >  OUT_MAX) out =  OUT_MAX;
    if(out < -OUT_MAX) out = -OUT_MAX;
    return out;
}

增量式(速度环典型,直接对 PWM 做增减):

// 增量式 PID:输出累加到 PWM 上,适合电机速度环
float Incremental_PID(float target, float measure){
    static float e0=0, e1=0, e2=0;         // 最近三拍误差:这次/上次/上上次
    static float pwm = 0;

    e0 = target - measure;
    float dU = Kp*(e0 - e1)                 // P:这拍与上拍偏差之差
             + Ki*e0                        // I:本拍偏差
             + Kd*(e0 - 2*e1 + e2);         // D:二阶差分
    pwm += dU;                              // 在上次基础上增减

    if(pwm >  PWM_MAX) pwm =  PWM_MAX;      // 输出限幅
    if(pwm < -PWM_MAX) pwm = -PWM_MAX;

    e2 = e1; e1 = e0;                       // 移位,准备下一拍
    return pwm;
}

💡 static 是关键

注意那几个 static 变量——integrallast_erre0/e1/e2pwm。PID 靠”记住上一次的状态”工作,这些变量必须跨函数调用保留,所以用 static(或者更规范地塞进一个结构体里)。新手最常犯的错就是把它们写成普通局部变量,每次进函数都清零,PID 直接废掉。

第一次调参:把一个电机的速度环调稳

理论讲完,最爽的来了——亲手调一次。目标极其朴素:让一个电机稳稳地跟住一个目标转速。不管你设 100 还是 200,它都能快速、平稳、不抖地达到并保持。

准备工作

  1. 架空轮子。把车架起来或拿一个电机单测,别让它在地上乱窜。第一次调参,安全第一,手边随时能断电。
  2. 接通编码器测速(感知那一篇讲过:用定时器编码器模式,把”单位周期的脉冲数”当速度反馈)。
  3. 定好控制周期。用定时器中断固定周期跑 PID,不要用 delay 凑!速度环常用 5~10ms(100~200Hz)。周期必须严格恒定,否则等效参数会乱漂。
  4. 把数据发出来看。这是调参的眼睛——用 printf 同时发”目标值”和”实际值”两路,丢给 VOFA+ 或串口示波器画成曲线。看不到波形,调参就是瞎子摸象。 具体怎么”看波形治百病”,是 PID 调参实战那一篇的核心内容,这里你先把曲线显示出来就行。

❗ 控制周期决定参数量级

提前打个预防针:PID 系数的大小强烈依赖控制周期。粗略地说,采样 1ms 时 $K_p$ 可能在”1″这个量级,采样 500ms 时 $K_p$ 可能在”0.01″量级——差出 100 倍。所以别人的参数你不能直接照抄,只能当个”数量级参考”。下面给的数值同理,是起点不是答案。

调参顺序:先 P、再 I、最后 D

有句流传几十年的工程口诀,背下来准没错:

参数整定找最佳,从小到大顺序查;先是比例后积分,最后再把微分加。

为什么是这个顺序?因为 P 决定基本响应、必须先立起来;I 是在 P 的基础上去消那条残余尾巴;D 最后用来压超调和抖动。顺序乱了,三者互相干扰,你根本判断不出是谁在捣乱。

📝 速度环常常只用 PI

提前说一句:电机速度环因为测速噪声大,很多时候只用 P 和 I,不加 D(加了 D 容易把测速毛刺放大成抖动)。所以下面的流程,重点是 P 和 I;D 你可以先不上,或者最后象征性地试一点点。

第一步:只调 P,找到”临界振荡”再退一档

把 $K_i$、$K_d$ 都设为 0,只留 P。给一个固定目标速度(比如目标 = 100)。

  • 从一个很小的 $K_p$ 开始,逐步加大
  • 你会看到:$K_p$ 小的时候,实际转速慢吞吞地往目标爬,还到不了(有稳态误差);
  • $K_p$ 加大,响应变快,但开始有超调(冲过头再回来);
  • 继续加大到某个值,曲线开始等幅振荡(绕着目标值上下来回、幅度既不收敛也不发散)——这个点叫临界点,记下此时的 $K_p$;
  • 然后把 $K_p$ 退回到临界值的 60%~70%(或者干脆乘 0.6)。这就是你的 P 起点。

这套”加到临界振荡再退一档”的做法,其实就是经典的临界比例度法(Ziegler-Nichols)的核心思想——它给你一个量级正确的起点,省得你从 0 瞎试。完整的 Z-N 换算表和起步估计,放到 PID 调参实战那一篇。

⚠️ 往临界振荡推是有风险的

真把车推到等幅振荡,电机/舵机有炸机风险。所以:先低速、先架空轮子、手边随时能断电。”减半/打六折”只是个起点,后面还要配合 I、D 微调。

第二步:加 I,把稳态误差那条尾巴收掉

P 调好后,你的转速大概率还差一点点到不了目标(稳态误差)。现在从一个很小的 $K_i$ 开始往上加:

  • 经验起点:$K_i$ 大约取 $K_p$ 的 1/10 ~ 1/5 起步试。
  • 加 I 之后观察曲线:稳态误差应该慢慢被”顶”上去,最终精确贴到目标线上。
  • $K_i$ 太小:尾巴消得太慢,半天贴不上目标。
  • $K_i$ 太大:开始超调、甚至振荡——这时往回退。

第三步(可选):加 D,压一压超调

如果加了 I 之后超调还是有点大,可以试着加一点点 D(从 0 缓慢加)来增加阻尼、压住超调。但前面说过,速度环测速噪声大,一旦看到曲线长毛刺、电机发抖,立刻停手回调。很多速度环这一步直接跳过。

调到这一步,你大概会在曲线上看到下面这几种典型现象,对照着调就行:

曲线现象 大概率是谁 怎么动
半天爬不到目标、慢吞吞 $K_p$ 太小 加大 $K_p$
来回振荡 / 越晃越大 $K_p$ 太大 减小 $K_p$
稳稳停住,但总差一丢丢(静差) 缺 I / $K_i$ 太小 加大 $K_i$
冲过头超调大、回不来 $K_i$ 太大 减小 $K_i$,或补一点 D
曲线长毛刺、电机发抖 $K_d$ 太大(放大噪声) 减小 $K_d$,或给 D 加滤波

一组真实的电赛参数(仅供感受量级)

光说”经验起点”太抽象,给你看一组 2024 年电赛巡线小车的真实实测参数,感受一下数量级(再强调一遍:这是别人车上的个案值,不能照抄,只能当参照):

项目 取值
$K_p$ 96
$K_i$ 0.01
$K_d$ 1.5
主循环周期 20ms
PWM 频率 约 36kHz
基础速度 260
速度限幅 ±550

🧩 从这组数读出的门道

注意 $K_i$ 被压到 0.01 这么小——这说明这辆车的环路主要靠 P 和 D,积分作用极弱。原因是巡线/转向场景里,积分稍微大一点,过弯时偏差就会被累加放大,把车顶出赛道。这正好印证了前面那句:”转向/方向环里积分要非常克制,甚至不用。” (来源:2024 电赛小车记

一个调不出来时的保命心态

最后讲个真事。有位作者做摄像头跟踪云台(X/Y 双轴 PID,以画面中心为目标、小球坐标为反馈),因为系统太复杂,比赛现场参数怎么都收敛不了,最后他承认”开环也未尝不可“——退一步用开环或分段控制保底,反而把任务完成了。

💡 PID 不是万能的

比赛就那么四天三夜,别在调参上耗死自己。如果一个闭环死活调不收敛,退一步用更简单的开环/查表/分段控制保底,往往比硬磕一个不收敛的 PID 更靠谱。先能跑、能拿分,再谈优雅。(经验来源

几个新手必踩的坑(先记下,进阶篇细讲)

这篇是入门,下面这些坑你先有个印象就好,真正的解法在 PID 进阶和 PID 调参实战两篇里:

  • static 状态变量被清零 → PID 失效。这是最低级也最高频的错。
  • 极性搞反(偏差符号或电机方向反了)→ 不是负反馈而是正反馈,车越偏越猛、直接发散。上电先用很小的增益验一下极性。
  • delay 凑控制周期、或周期被中断打乱 → 相当于采样忽快忽慢,等效参数乱漂,怎么调都不对。一定用定时器固定周期。
  • 照抄别人参数却换了周期/电机/电压 → 量级完全不对。
  • 位置式忘了给积分限幅 → 积分饱和,超调到天上。
  • D 直接加大 → 放大测速噪声,电机发抖。

小结

这一篇,我们把 PID 从”玄学”拉回了”直觉”:

  • 它是一套负反馈纠错:目标减实际得偏差,再用三路力度去纠。
  • P 看现在(管快)、I 看过去(管准)、D 看未来(管稳)——记牢是”P 快、I 准、D 稳”,别被对调的版本骗了。
  • 位置式输出绝对量、适合舵机/方向环;增量式输出增量、适合电机速度环,且天然规避显式积分饱和。
  • 调参顺序铁律:先 P、再 I、最后 D,参数从小到大。第一次就拿一个电机的速度环练手,把曲线发到上位机上盯着调。

你现在已经能让一个电机稳稳听话了。但一辆车要跑起来,光有速度还不够——它还得知道”往哪偏了、该怎么转”,这就要把方向环也加进来,让它和速度环串成两级协同工作;而真正让车在赛道上不翻不冲,还得靠一整套工程补丁。这些,我们下一篇 PID 进阶接着聊。



本文由 云间辞 原创,发布于 HBZGC 的博客

转载请保留链接: https://cloudlay.cn/nuedc-car-06-pid-basics/

暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇