从原神开小火车到 PID:让系统自己稳下来的控制算法

《原神》7.0 的「程准列车驭法」让旅行者驾驶列车,在限定时间内安全抵达目标站点。真正玩起来,手上要同时盯几件事:挡位会改变加速度,负挡位负责制动;上坡会掉速,下坡容易超速,暴风雪会增加阻力、遮挡视野;限速区间要求速度落在指定范围,到站时还要尽量停在标记位置;频繁换挡会让动力核心升温,过热以后列车会掉到零挡滑行并影响评分。

教学画面里能看到速度和挡位仪表、两侧控制按钮、高亮路段、停车标记和暴风雪。把它们分别对应到目标、测量、输入和扰动,就能从这个玩法理解反馈控制。

程准列车驭法教学截图 1:列车界面和速度仪表

程准列车驭法教学截图 2:轨道上的速度控制区间

程准列车驭法教学截图 3:列车接近目标路段

程准列车驭法教学截图 4:动力核心升温提示

程准列车驭法教学截图 5:终点前的停车标记

程准列车驭法教学截图 6:轨道提示和前方路况

程准列车驭法教学截图 7:鸣笛或交互提示

程准列车驭法教学截图 8:暴风雪遮挡视野

这个玩法很容易让人想到 PID,因为玩家手上其实在做一件控制器要做的事。看到速度低了就升挡,快到限速线就提前收油或刹车,上坡多给一点动力,下坡提前压住速度,到站前还要判断现在离停车点有多远、速度是不是太大。玩家没有列车完整的物理模型,也不会计算坡度、阻力、质量和轨道曲率;玩家只是反复看反馈,再调整输入。

降挡以后,列车还会继续滑行;升挡以后,速度也要过一会儿才起来。轨道坡度、暴风雪阻力和动力核心温度还会不断改变手感。PID 的价值就在这里:它不要求先把系统彻底建模,而是把「偏了多少、偏了多久、正在怎么变」这三件事压成一个持续更新的控制量。

原神列车玩法里的控制对象、反馈和扰动

把游戏手感翻译成控制问题

控制问题里有几个词,放进列车玩法就很好懂。

目标值是希望系统达到的状态。列车进入限速区时,目标是把速度控制在指定范围;靠近终点时,目标变成在标记附近停稳。换到别的设备上,它也可以是目标温度、目标角度或目标电流。

测量值是系统现在反馈出来的状态。游戏里可以直接看到速度、位置、挡位、动力核心温度,以及前方路况。真实设备里,这些反馈来自传感器,比如编码器、陀螺仪、雷达、温度计或电流采样。

误差是目标值和测量值之间的差。速度低于目标,误差为正;速度高于目标,误差为负。到站停车时,距离标记还有多远、当前速度还有多大,也都在决定误差该怎么理解。

控制量是系统能实际操作的输入。列车里是挡位和刹车;船舶自动舵里是舵角;电机里可能是电压、电流或 PWM 占空比。

扰动是系统外部或内部突然改变的条件。上坡、下坡和暴风雪会让列车受力变化;载重、电压、地面摩擦变化也会把很多设备推离原来的状态。控制器要在扰动还存在的时候,把系统拉回目标附近。

控制器每次先读测量值,拿它和目标值相减,再把误差换成下一次输出。

目标、误差、控制器、列车和传感器之间的循环关系

这个计算会每隔一小段时间重来一遍。系统受了扰动,下一轮测量会把变化带回来;上一轮给多了,下一轮就会看见自己冲过头;上一轮给少了,误差会继续存在,控制器也会继续补。

用 PID 解释列车活动,需要留一个边界:活动本身不一定真的用了 PID。这里能确定的是,它暴露了同一类控制问题:有限输入、连续反馈、物理惯性、外界扰动和目标追踪。游戏的实际实现可能只是规则脚本和手感参数,但玩家在操作时,脑子里确实在做一种反馈控制。

为什么只开和关容易来回摆

最容易写出来的控制逻辑,是开关控制,也常被叫作 bang-bang control:

如果速度低于限速区间:升挡
如果速度高于限速区间:刹车或降挡

限速区间很宽、坡度变化很小的时候,这种逻辑也许够用。目标范围一窄,问题就出来了:列车已经在下坡加速,速度刚碰到上限才刹车,下一秒可能已经冲出区间;终点标记已经在眼前才降挡,惯性还会把车往前带一段。

开关控制容易围绕目标来回摆动,PID 尝试提前减小修正力度

控制系统里经常看一个阶跃响应:目标突然从 0 跳到 1,系统怎么追上去。这里会出现几个常用指标。

  • 上升时间看系统多快接近目标。
  • 超调看系统冲过目标多少。
  • 稳定时间看系统多久以后不再大幅摆动。
  • 稳态误差看系统稳定下来以后,还差目标多少。

这些词听起来像考试名词,实际就是开小火车时的手感。加速太慢,赶不上限时;刹太晚,会冲过限速线;一直在目标附近抖,评分不稳;最后总停在标记前一点点,就是稳态误差还没消掉。

PID 的三项各管一件事

PID 是 Proportional、Integral、Derivative 的缩写,通常译作比例、积分、微分。MathWorks 的 PID 说明把它定义成一种用三项来调整控制器输出、缩小目标值和实际响应差距的控制方法;它也给出了常见的并联形式:

u(t) = Kp * e(t) + Ki * ∫ e(t) dt + Kd * de(t)/dt

这里 e(t) 是误差,u(t) 是控制器输出,Kp、Ki、Kd 是三项各自的权重。工程实现里还常会给微分项加滤波,MathWorks 文档里的连续形式就把 derivative filter time 单独列了出来。

这行公式不用急着当数学看。它问的是三件很具体的事。

PID 三项分别看当前误差、累计误差和变化趋势

P:现在差多少

P 项只看当前误差。

列车速度离目标差很多,就给大一点挡位;差一点点,就给小一点修正。比如目标速度在中速区间,当前速度还很低,P 项会推动输出加大;速度已经贴近区间上沿时,它给出的修正就应该变小。

P 项的直觉很像「力度跟差距成比例」。它反应快,结构也简单,所以很多系统光靠 P 就能有基本可用的手感。缺点也明显:如果系统存在持续阻力或偏置,单靠 P 往往会留下稳态误差。

比如列车在上坡,二挡刚好抵消一部分阻力,但速度总卡在目标下方一点。误差只剩一点点时,P 项给出的额外动力也很小,最后可能就停在「差一点但一直差」的状态。

I:过去欠了多少账

I 项把误差按时间累加起来。

如果速度只低一点,但已经低了很久,积分项会越来越大,直到给出足够的额外动力把系统顶回目标。列车长时间上坡,或者暴风雪让阻力变大时,P 项可能只看到「差一点」;I 项会把这段持续偏差记下来,提醒控制器继续补。

积分项最擅长消除稳态误差。它也最容易闯祸,因为它会记仇。控制器输出有上限时,系统明明已经满功率了,误差如果还存在,积分项可能继续累加。等系统终于接近目标,积分项已经攒得太大,控制器会继续猛推一段时间,导致严重超调。这就是积分饱和,或者 integrator windup。

MathWorks 的 anti-windup 示例把这个问题落在执行器饱和上:控制信号超出了执行器范围,继续增加控制输出已经不会改变对象,积分器却还在累加,后面恢复会很慢。实际代码里通常要配合输出限制、积分限幅、条件积分或 back-calculation 这类 anti-windup 逻辑。

D:现在变化得有多快

D 项看误差的变化率,或者更常见地看测量值的变化率。

目标速度不变时,列车还没超速,但速度正快速上升,D 项就可以减小继续加速的输出。它利用的是已经测到的变化率,不是预知前方限速或红灯;根据前方轨道提前降低目标速度,属于目标规划或前馈的职责。停车控制若以位置为反馈,位置变化率又能反映当前速度。

微分项可以减少超调、抑制振荡,但它对噪声很敏感。速度显示如果一会儿跳 48,一会儿跳 51,一会儿跳 49,微分项会把这些短时间变化放大,控制输出就可能抖得厉害。Control Guru 的导数噪声文章也强调,测量值里的随机波动会被微分计算放大,并反映到控制器输出上。

这就是为什么工业实现里常见「微分作用在测量值上」和「微分项加滤波」。APMonitor 的 PID 说明也提到,直接对误差求导会在设定值突然变化时产生 derivative kick,实践中常用过程变量的导数来避免设定值跳变把控制输出猛地踢到上下限。

调参是在选一种性格

三个增益一起开大,系统通常先变得激进和抖动。三个增益互相影响,每个系统还有自己的惯性、延迟、阻尼、输入上限和噪声。

调参改变的是系统追目标时的速度、超调和稳定方式

Kp 加大,系统会更积极地追目标。它通常能缩短上升时间、降低一部分稳态误差,但太大时会超调和振荡。开列车时这像是手很重,速度一低就猛升挡,一高就猛刹,来回折腾。

Ki 加大,系统会更执着地消除长期偏差。它能把「总差一点」推回目标,但太大时容易累过头,尤其在输出已经打满时制造 windup。上坡长时间追不上目标速度时,积分能帮忙;到了坡顶或进入下坡,积累出来的额外动力也可能把速度推过头。

Kd 加大,系统会更怕趋势过猛。它能提前刹住超调,减少摆动,但太大时会让响应变慢,也会把测量噪声放大成控制输出抖动。

调 PID 更像给系统选性格:激进、稳重、迟钝、敏感、怕过头、怕欠账。Ziegler-Nichols、IMC、继电反馈、自整定和模型辨识都是调参方法,最后仍要回到系统的真实约束:执行器能给多少力,传感器有多少噪声,延迟有多大,超调的代价能不能接受。

Åström 和 Murray 的 Feedback Systems把 PID 单独放在一章里,也把 windup、实现和调参放在同一章。PID 公式简单,难点常在系统、限制和实现。

玩家其实在调一个混合目标

列车活动的目标会随着路段改变。

不限速路段里,玩家想尽快接近合理高速,同时避免动力核心温度过高。限速区间里,目标是让速度落在指定颜色对应的范围。靠近终点时,目标从「跑得快」切到「停得准」,位置误差和当前速度一起决定怎么操作。上坡、下坡、暴风雪又不断改变列车动力和阻力的关系。

这类场景通常要分层处理。

  • 速度控制器负责把速度拉到目标范围。
  • 位置或停车控制器在终点前生成一个随距离变化的目标速度。
  • 温度或换挡限制负责给控制输出加上安全边界。
  • 坡度、暴风雪、弯道和限速牌作为扰动或外部规则进入系统。

玩家操作时,脑子里也大致是这样分层的。看到前面是限速门,先把目标速度降下来;发现下坡还在加速,就提前给刹车;发现上坡掉速,就升挡;温度快高了,就减少频繁换挡。这个过程不用写公式,但它一直在用反馈。

列车玩法可以拆成速度目标、停车目标、温度限制和路况扰动

有些关卡玩起来会有「提前量」的感觉。控制带惯性的对象时,好的操作会在趋势还没变成错误时先减小输入,避免冲过头以后再猛改。D 项的直觉就是提前量;P 项负责当前差距;I 项负责长期偏差。

写成代码时,要把现实限制放进去

最朴素的离散 PID 不难写。控制器每隔 dt 秒运行一次,读取目标和测量值,更新误差、积分和微分,再给出输出。

type PidState = {
  integral: number;
  previousError: number;
  previousMeasurement: number;
};

function updatePid(
  target: number,
  measurement: number,
  dt: number,
  state: PidState,
) {
  const error = target - measurement;

  state.integral += error * dt;
  const derivative = (error - state.previousError) / dt;

  const output =
    Kp * error +
    Ki * state.integral +
    Kd * derivative;

  state.previousError = error;
  state.previousMeasurement = measurement;

  return output;
}

这段能解释公式,但离可用还差几步。

第一,dt 不能忽略。采样间隔变了,积分和微分都会变。如果游戏帧率、控制循环周期或传感器上报频率不稳定,控制器要使用真实经过时间,或者把控制循环固定在稳定频率。

第二,输出要限幅。列车挡位有最高和最低,电机电流、阀门开度和推进功率也都有上限。控制器算出 180% 功率没有物理意义,最终必须裁到执行器能接受的范围。

第三,积分要配合 anti-windup。输出已经打到上限时,积分项如果继续按误差累加,后面会拖着系统过冲。常见做法包括给积分项限幅;更稳的做法是当输出饱和且误差还在把它往饱和方向推时,暂停积分,或者把饱和前后的差额回灌给积分器。

第四,微分项要处理噪声和设定值跳变。实践中常把 D 作用在测量值上,避开直接对误差求导带来的设定值跳变,并给微分项加低通滤波。否则目标值一改,误差瞬间跳变,输出可能立刻踢到上下限;测量值只要有抖动,D 项也会跟着抖。

实际 PID 循环通常会在公式外面再加几道护栏。

实际 PID 循环还要处理采样周期、输出限幅、积分饱和和微分噪声

这些边界也能解释为什么 PID 看似不到十行,真正调好却很花时间。调 Kp、Ki、Kd 之外,还要一起处理采样周期、输出限制、滤波、目标生成方式、切换逻辑和故障保护。

PID 为什么能用这么久

Nicolas Minorsky 在 1922 年发表的自动舵稳定性论文,标题是「自动转向物体的方向稳定性」,刊登在《Journal of the American Society for Naval Engineers》。这篇论文的重要性在于,它把经验舵手的直觉写成了可分析、可实现的反馈控制。一百多年以后,PID 仍然出现在温控、电机、化工、机器人、飞行控制、巡航、云台和各种嵌入式设备里。

这套方法能留到今天,靠的是几个工程优点。

PID 不要求先得到完整模型,但仍要了解反馈方向、执行器能力和主要动态。能测误差、能调整输入,并不保证任意对象都能被稳定控制;延迟、饱和或错误的增益也可能把系统推向振荡。它适合许多模型不够精确、但局部反馈规律可用的对象。

PID 足够透明。P、I、D 三项能直接对应到当前偏差、长期偏差和变化趋势,调参结果也能从响应曲线里看出来。和黑箱优化相比,它更容易被工程师接受、调试和兜底。

PID 对硬件友好。它只需要少量状态和简单计算,适合微控制器、PLC、驱动器和实时系统。MathWorks 也把 PID 从建模、自动整定、代码生成到 PLC/PAC 结构化文本都放进控制工具链里,说明它在工程落地里仍然是基本能力。

PID 更适合作为基础反馈层。

强非线性、长延迟、多输入多输出、强耦合、约束很多、目标频繁切换的系统,通常需要 PID 之外的控制层。以列车活动为例,速度、位置、温度、鸣笛、视野结冰、评分策略和路线规划分别属于不同约束;工程实现会把状态机、前馈、轨迹规划、模型预测控制或多层控制架构叠在一起,让 PID 负责其中一段局部调节。

理解 PID 的价值也在这里:先看目标、测量值、误差和输出,再看哪个环节需要更复杂的方法。后面的状态估计、路径规划或模型预测控制,很多时候还在回答同一个问题,只是可用的信息更多、约束更硬、计算量更大。

再回到那列车

「程准列车驭法」把反馈控制的几个基本难点摆到了手上:目标会变,输入有限,系统有惯性,路况会扰动,过热会限制操作,最后还要停得准。

P 看当前差距,I 累积持续偏差,D 利用变化率抑制过猛趋势。这个分工能解释不少操作手感,但不能代替游戏的路线规划、温度限制和离散换挡策略;实际控制效果,还要靠系统条件、调参和测试确认。

下次看到温控、电机调速或云台稳定,可以先找目标、反馈和可调输入,再问控制器怎样处理延迟、噪声与上限。PID 公式很短,真正决定它是否可用的,往往就是这些公式之外的条件。