Part IV · 第 13 章 反馈控制 · 13.1Part IV · Ch. 13 Feedback Control · 13.1

PID 控制PID Control

机器人里几乎每一个电机背后都跑着一个 PID:比例项看现在的误差,积分项记住过去,微分项预判将来。这一节在一个受重力的关节上把三项拆开来玩,再看真实硬件上它会怎样出问题:微分冲击与噪声、饱和与积分饱和(windup)、采样率;最后学会整定(Ziegler–Nichols 与手动)、前馈,以及伺服驱动器和无人机里层层嵌套的串级回路。 Behind almost every motor in a robot runs a PID loop: the proportional term looks at the present error, the integral remembers the past, the derivative anticipates the future. This lesson takes the three terms apart on a joint under gravity, then shows how they misbehave on real hardware (derivative kick and noise, saturation and integrator windup, sample rate), and finally how to tune them (Ziegler–Nichols and by hand), how feed-forward helps, and how servo drives and drones nest loops inside loops.

约 45 分钟~45 min 前置:2.1 导数 · 2.3 积分 · 2.5 常微分方程 · 8.3 质量矩阵与科氏力 · 9.4 执行器与传感器Prereqs: 2.1 Derivatives · 2.3 Integration · 2.5 ODEs · 8.3 Mass Matrix & Coriolis · 9.4 Actuators & Sensors 7 个交互7 interactives

    1为什么机器人需要它Why robots need this

    规划器说"关节 2 在 0.5 秒后要到 60°",电机却只听得懂电压和电流。中间隔着摩擦、重力、没建模的负载、会变的电池电压。要让关节真的到达 60° 并停在那里,就得不停地测量、比较、修正:这就是反馈。PID 是反馈控制里最简单、最普遍的形式,它只看一个数:误差 \(e=q_d-q\)(目标角减实际角)。

    • 每一个关节伺服:工业臂、协作臂、舵机里都有位置环、速度环、电流环,几乎全是 P、PI 或 PID。
    • 无人机:姿态角的 P 环套着角速度的 PID 环,几百赫兹到上千赫兹地跑。
    • 足式机器人:强化学习策略常常只输出"关节目标角",真正驱动电机的是每个关节上的 PD 环。
    • 其他一切:轮速、夹爪力、3D 打印机喷头温度、云台……

    Åström 与 Murray 的教科书引用了一项对炼油、化工和造纸行业一万一千多个控制器的调查(Desborough 与 Miller,2002):97% 的常规控制器用的是 PID 算法,其中很多实际上只是 PI。它好用,是因为不需要模型就能工作;它难用,是因为真实硬件的饱和、噪声、延迟会让教科书公式出各种问题。2.5 节的 FIG 5 已经用 PD 控制过一个受重力的关节,并看到它停在离目标差一截的地方。这一节从那里接着走。

    The planner says "joint 2 must be at 60° in 0.5 s", but the motor only understands voltage and current. In between sit friction, gravity, unmodelled payloads and a battery whose voltage sags. To make the joint actually reach 60° and stay there you must keep measuring, comparing and correcting: that is feedback. PID is its simplest and most common form, and it looks at a single number, the error \(e=q_d-q\) (target angle minus actual angle).

    • Every joint servo: industrial arms, cobots and hobby servos all run position, velocity and current loops, nearly always P, PI or PID.
    • Drones: a P loop on attitude angle wrapped around a PID loop on angular rate, running at hundreds to thousands of hertz.
    • Legged robots: reinforcement-learning policies often output only "joint target angles"; what actually drives the motors is a PD loop on every joint.
    • Everything else: wheel speed, gripper force, the temperature of a 3D printer's hot end, camera gimbals…

    Åström and Murray's textbook cites a survey of more than 11,000 controllers in the refining, chemical and pulp-and-paper industries (Desborough and Miller, 2002): 97% of the regulatory controllers used a PID algorithm, many of them in fact just PI. It is popular because it works without a model; it is tricky because saturation, noise and delay on real hardware break the textbook formula in several ways. FIG 5 of Lesson 2.5 already controlled a joint under gravity with PD and saw it stop short of the target. This lesson picks up from there.

    2直觉:现在、过去、将来Intuition: present, past, future

    想象你伸直手臂提着一桶水,要把它举到某个高度。差得越多,越用力:这是比例项,像一根弹簧。可是桶有重量,弹簧只有在被拉开一截时才出力,所以你会停在比目标低一点的地方,那一截正好让弹簧的力等于桶的重量。于是你会"越等越用力":只要还差一点,就持续加力,直到差距消失。这是积分项,它记住了过去所有的误差。最后,快到的时候你会提前收力,免得冲过头:这是微分项,它看误差变化的速度,预判将来。三项加起来就是

    Imagine holding a bucket of water at arm's length and lifting it to a mark. The further off, the harder you push: that is the proportional term, a spring. But the bucket has weight, and a spring only pushes when it is stretched, so you settle a little below the mark, exactly where the spring force equals the weight. So you push harder the longer you wait: as long as you are still short, keep adding effort until the gap is gone. That is the integral term, which remembers all past error. Finally, as you get close you ease off early so you do not overshoot: that is the derivative term, which watches how fast the error changes and anticipates the future. Together:

    $$ u(t)\;=\;\hl{P}{K_p\,e(t)}\;+\;\hl{I}{K_i\!\int_0^t e(s)\,ds}\;+\;\hl{D}{K_d\,\dot e(t)},\qquad e=q_d-q . $$

    三项从左到右依次对应现在、过去和将来。这里的输出 \(u\) 是关节力矩 \(\tau\)(N·m)。被控的就是 2.5 节那根关节:\(J\ddot q+b\dot q+mg\ell\sin q=\tau\),\(J=0.5\) kg·m²,\(b=0.1\) N·m·s/rad,\(mg\ell=4\) N·m,\(q\) 从竖直向下量起。电机最多给 ±10 N·m。下图是实时仿真:拖动紫色手柄改目标角,用滑块调三个增益,或者点预设。三项各自的贡献画在右下,也列在下方的条形图里。

    From left to right the three terms stand for the present, the past and the future. Here the output \(u\) is the joint torque \(\tau\) (N·m). The plant is the joint from Lesson 2.5: \(J\ddot q+b\dot q+mg\ell\sin q=\tau\) with \(J=0.5\) kg·m², \(b=0.1\) N·m·s/rad, \(mg\ell=4\) N·m, and \(q\) measured from hanging straight down. The motor delivers at most ±10 N·m. The figure is a live simulation: drag the purple handle to move the target, set the three gains with the sliders or pick a preset. Each term's contribution is plotted at the lower right and listed in the bars below.

    FIG 1三个旋钮:一根受重力的关节,实时运行Three knobs: a joint under gravity, running live
    关节:拖动紫色手柄设目标JOINT · drag the purple handle
    P 项 KpeP term Kpe
    I 项 Ki∫eI term Ki∫e
    D 项 −Kdq̇D term −Kdq̇
    电机力矩 umotor torque u
    左:琥珀色是关节,紫色虚线是目标角 \(q_d\)。右上:琥珀色 \(q(t)\),紫色虚线 \(q_d(t)\)(单位:度)。右下:红色 P 项、绿色 I 项、蓝色 D 项、青色总力矩 \(u\)(N·m),虚线是 ±10 N·m 的电机上限。条形图量程 ±12 N·m。这里的 D 项取的是测量值的导数 \(-K_d\dot q\)(目标不动时它就等于 \(K_d\dot e\),FIG 2 解释为什么这样取),积分项带"条件积分"抗饱和(FIG 3 解释)。把 \(K_i\) 调到 0 时,积分状态也一并清零。"推一下"给关节一个突然的角速度;"负载"在末端挂一个重物(\(mg\ell\) 增加 2 N·m,惯量增加 0.16 kg·m²)。 Left: the amber link is the joint, the purple dashed line the target \(q_d\). Upper right: amber \(q(t)\) and purple dashed \(q_d(t)\) in degrees. Lower right: red P term, green I term, blue D term and cyan total torque \(u\) (N·m); the dashed lines are the ±10 N·m motor limit. The bars span ±12 N·m. The D term here is taken on the measurement, \(-K_d\dot q\) (equal to \(K_d\dot e\) while the target stands still; FIG 2 explains why), and the integral has "conditional integration" anti-windup (explained in FIG 3). Setting \(K_i\) to 0 also clears the integral state. "Push" gives the joint a sudden angular velocity; "payload" hangs a weight on the tip (it adds 2 N·m to \(mg\ell\) and 0.16 kg·m² to the inertia).
    试一试Try this
    • 只用 P(\(K_i=0\))时手臂在默认目标附近晃个不停。保留 \(K_p>0\),加一点 \(K_d\)(大于 0)让它停下来,目标设在离竖直向下 30° 到 150° 之间(重力矩足够大的地方):它停在离目标超过 2° 的位置(静止 1 秒以上)。把 \(K_p\) 加大,差距缩小了,但消失了吗?With P alone (\(K_i=0\)) the arm keeps swinging around the default target. Keep \(K_p>0\), add some \(K_d\) (above 0) so it settles, with the target between 30° and 150° from hanging down (where gravity torque is large): it comes to rest (for at least 1 s) more than 2° short. Raise \(K_p\): the gap shrinks, but does it vanish?
    • 打开积分(\(K_i>0\)),在同样受重力的目标处让手臂静止 1 秒,误差小于 0.2°。下方绿色的 I 项最后停在多少 N·m?它和重力矩 \(mg\ell\sin q_d\) 比如何?Turn on the integral (\(K_i>0\)) and let the arm rest for 1 s at a similarly gravity-loaded target with an error below 0.2°. Where does the green I term end up, in N·m? Compare it with the gravity torque \(mg\ell\sin q_d\).
    • 在上一步的状态下(\(K_i>0\)、静止、误差小于 0.5°)点"负载":手臂先下沉超过 1°,然后在 6 秒内自己爬回到 0.2° 以内。谁做了这件事?From the previous state (\(K_i>0\), at rest, error below 0.5°) press "payload": the arm sags by more than 1°, then climbs back within 0.2° within 6 s on its own. Which term did that?
    核心洞见Key insight

    P 是弹簧,D 是阻尼,I 是记忆。只有 P 和 D 时,静止的关节上 \(\dot q=0\),D 项为零,唯一能抵消重力的是 P 项 \(K_pe\),所以误差必须不为零:\(e_{ss}\approx mg\ell\sin q_d/K_p\)。I 项不一样:只要误差不为零,它就一直在变,所以手臂只可能在 \(e=0\) 的地方停下,那时积分正好等于重力矩。代价是:积分有"惯性",加得太猛(试试 PI 预设,\(K_d=0\))会让关节振荡甚至发散。

    P is a spring, D is a damper, I is a memory. With only P and D, a resting joint has \(\dot q=0\), the D term vanishes, and the only thing holding up gravity is the P term \(K_pe\), so the error cannot be zero: \(e_{ss}\approx mg\ell\sin q_d/K_p\). The I term is different: as long as the error is non-zero it keeps changing, so the arm can only come to rest where \(e=0\), and there the integral exactly equals the gravity torque. The price: the integral has "momentum", and too much of it (try the PI preset with \(K_d=0\)) makes the joint oscillate or even diverge.

    3动手玩:真实硬件上的 D 和 IPlay: D and I on real hardware

    微分项:冲击、噪声与滤波The derivative: kick, noise and filtering

    控制器是一段每 1 ms 跑一次的代码,导数只能用差分算:\(\dot e\approx(e_k-e_{k-1})/T_s\)。2.1 节和 9.4 节已经看到,差分会把测量噪声除以一个很小的 \(T_s\),放大成很大的速度误差。PID 里这还要再乘 \(K_d\)。另一个问题来自目标:目标角突然跳 60°,\(e\) 在一个采样周期里跳 1.05 rad,\(K_d\Delta e/T_s\) 就是几千 N·m 的尖峰,叫微分冲击(derivative kick)。

    两个标准对策:(1) 对测量值求导:用 \(-K_d\dot q\) 代替 \(K_d\dot e\)。目标不动时两者相同,目标跳变时前者不受影响;(2) 给 D 项加一阶低通,截止频率 \(f_c\)。下图是同一个关节、同一组 \(K_p=30,\ K_i=20\),0.3 s 时目标从 0 跳到 60°,编码器 12 位(每圈 4096 格),读数再叠加标准差 \(\sigma\) 的噪声。

    A controller is a piece of code that runs every 1 ms, so the derivative has to be a difference: \(\dot e\approx(e_k-e_{k-1})/T_s\). Lesson 2.1 and Lesson 9.4 already showed that differencing divides measurement noise by a tiny \(T_s\) and turns it into a large velocity error. In a PID it is then multiplied by \(K_d\). A second problem comes from the target: when it jumps by 60°, \(e\) jumps by 1.05 rad within one sample, and \(K_d\Delta e/T_s\) is a spike of thousands of N·m, the derivative kick.

    Two standard remedies: (1) differentiate the measurement: use \(-K_d\dot q\) instead of \(K_d\dot e\). The two agree while the target stands still, and the first one ignores target jumps; (2) low-pass filter the D term with cutoff \(f_c\). The figure uses the same joint and the same \(K_p=30,\ K_i=20\); at 0.3 s the target jumps from 0 to 60°, the encoder has 12 bits (4096 counts per turn), and the reading carries extra noise of standard deviation \(\sigma\).

    FIG 2数字控制器里的微分项The D term in a digital controller
    上:琥珀色是关节角 \(q\),紫色虚线是目标。下:青色是实际送给电机的力矩 \(u\)(被限制在 ±10 N·m,虚线),琥珀色细线是控制器"想要"的指令 \(v\)(饱和之前),超出画面时它的峰值写在读数里。"力矩波动"是 \(t\ge1.8\) s(手臂已基本停稳)之后 \(u\) 的标准差:电机在静止时还在"嗡嗡"抖的程度。噪声序列固定(同一个随机种子),所以每次结果一样。 Top: amber is the joint angle \(q\), the purple dashed line the target. Bottom: cyan is the torque \(u\) actually sent to the motor (limited to ±10 N·m, dashed), and the thin amber line is the command \(v\) the controller would like (before saturation); when it leaves the plot its peak is given in the readout. "Torque ripple" is the standard deviation of \(u\) for \(t\ge1.8\) s (once the arm has nearly settled): how much the motor still buzzes at rest. The noise sequence is fixed (same random seed), so results are repeatable.
    试一试Try this
    • 切换到"对误差求导",不开滤波:控制器想要的指令峰值超过 1000 N·m,是电机上限的 100 多倍。切回"对测量值求导",峰值降到多少?Switch to "derivative of error" with the filter off: the command the controller wants peaks above 1000 N·m, over 100 times the motor limit. Switch back to "derivative of measurement": what does the peak drop to?
    • 保持"对测量值求导",调滤波截止频率 \(f_c\),让力矩波动小于 0.3 N·m,同时超调小于 15%。再把 \(f_c\) 降到 1 Hz 以下:为什么超调反而变大了?Keep "derivative of measurement" and pick a filter cutoff \(f_c\) so the torque ripple is below 0.3 N·m while the overshoot stays below 15%. Then push \(f_c\) below 1 Hz: why does the overshoot grow again?

    积分项:饱和与积分饱和(windup)The integral: saturation and windup

    电机力矩有上限。上限一到,控制器再"想要"更大的力矩也没用,可积分项不知道这件事,它照样把误差一点点加上去。等手臂终于追上目标,积分里已经存了一大笔"欠账",必须先反向误差很久才能还清:结果是巨大的超调。这叫积分饱和(integrator windup)。在机器人上特别常见:大步长的目标、手臂被人或障碍物挡住、关节碰到限位、急停后恢复。

    两种标准的抗饱和(anti-windup):条件积分(clamping):输出已经饱和、并且误差还在把它往饱和方向推时,暂停积分;反算(back-calculation):把"想要的"与"实际给的"之差 \(u-v\) 乘上增益 \(k_t=1/T_t\) 反馈进积分器,饱和期间积分被拉回到刚好不饱和的值。下图:增益 \(K_p=30\)、\(K_i=30\)、\(K_d=5\),目标在 0.2 s 时从 0 跳到 90°。"被手挡住"情景下,前 2.5 s 有一只手把手臂挡在 30°。

    Motor torque has a limit. Once it is reached, asking for more does nothing, but the integral does not know that and keeps adding up the error. By the time the arm finally catches up, the integral holds a large "debt" that can only be paid back by a long stretch of opposite error: a huge overshoot. This is integrator windup. It is common on robots: large target steps, an arm held back by a person or an obstacle, a joint against its limit, resuming after an emergency stop.

    Two standard anti-windup schemes: clamping (conditional integration) pauses the integral while the output is saturated and the error pushes it further into saturation; back-calculation feeds the difference \(u-v\) between "delivered" and "wanted" back into the integrator with gain \(k_t=1/T_t\), so during saturation the integral is pulled back to the value that just avoids saturating. In the figure, the gains are \(K_p=30\), \(K_i=30\), \(K_d=5\), and the target steps from 0 to 90° at 0.2 s. In the "held by a hand" scenario a hand blocks the arm at 30° for the first 2.5 s.

    FIG 3积分饱和与抗饱和Windup and anti-windup
    关节(按"播放")JOINT · press Play
    右图三栏:(1) 琥珀色关节角 \(q\)、紫色虚线目标;(2) 青色实际力矩 \(u\),琥珀色细线是饱和前的指令 \(v\),虚线是力矩上限;(3) 绿色是积分项 \(I\)(N·m),虚线同样是力矩上限:绿线远远冲出虚线,就是积分饱和。竖虚线是播放时间。超调与调节时间从目标跳变(或手松开)起算,调节时间用 ±2% 带。 Right, three panels: (1) amber joint angle \(q\), purple dashed target; (2) cyan applied torque \(u\), thin amber the pre-saturation command \(v\), dashed the torque limit; (3) green the integral term \(I\) (N·m), again with the torque limit dashed: the green line running far past the dashed one is windup. The vertical dashed line is the playback time. Overshoot and settling time count from the step (or the release), with a ±2% band.
    试一试Try this
    • 选"被手挡住"、不抗饱和:手一松,超调超过 100%,手臂甚至甩过头顶。手被挡住的 2.3 秒里,积分涨到了力矩上限的多少倍?Choose "held by a hand" with no anti-windup: on release the overshoot is above 100% and the arm swings right over the top. During the 2.3 s it was held, how many times the torque limit did the integral reach?
    • 同一个情景,换成条件积分或反算:超调小于 5%,并且松手后 2.5 秒内进入 ±2% 带。反算的 \(k_t\) 取太小或太大会怎样?Same scenario with clamping or back-calculation: overshoot below 5% and inside the ±2% band within 2.5 s of the release. What happens when back-calculation's \(k_t\) is too small, or too large?
    核心洞见Key insight

    教科书里的 PID 是一个线性系统,真实的执行器不是:它有上限、有噪声、按采样周期工作。微分项的问题出在"高频"(目标跳变和噪声都是高频),所以对策是不让高频进入 D(对测量值求导、低通);积分项的问题出在"控制器以为在出力、其实没有",所以对策是让积分知道执行器的真实输出(条件积分、反算)。工程里的 PID 代码,一大半篇幅都在处理这两件事。

    The textbook PID is a linear system; a real actuator is not: it saturates, it is noisy, it works in samples. The derivative's trouble lives at "high frequency" (target jumps and noise are both high-frequency), so the cure is keeping high frequencies out of D (differentiate the measurement, low-pass it). The integral's trouble is "the controller believes it is pushing when it is not", so the cure is telling the integral what the actuator really delivered (clamping, back-calculation). Most of the lines in a production PID implementation deal with these two things.

    4第二、第三视角:怎样整定,多快采样Second and third views: how to tune, how fast to sample

    整定:Ziegler–Nichols 的"临界增益"实验Tuning: the Ziegler–Nichols ultimate-gain experiment

    没有模型时怎样选三个增益?Ziegler 与 Nichols(1942)给了一个纯实验的办法:先只用 P,把 \(K_p\) 一点点加大,直到系统出现不增也不减的等幅振荡。这时的增益叫临界增益 \(K_u\),振荡周期叫 \(T_u\)。两个数刻画了"系统离不稳定还有多远、有多快",查表就得到 PID 增益。

    这个实验需要一个"加大 \(K_p\) 终究会失稳"的对象。FIG 1 的关节是一个阻尼极小的二阶系统(没有延迟),加多少 P 都只是振荡得更快,永远不会失稳,不适合。真实伺服里常见的是用电压驱动的直流电机关节:沿用 9.4 节的电机(\(k_t=k_e=0.06\),\(R=0.5\ \Omega\),\(J_m=10^{-4}\) kg·m²),经 50:1 减速器带动同一根 0.5 kg·m² 的连杆(水平放置,不受重力)。反电动势提供了很强的阻尼,再加上 10 ms 的回路延迟(滤波、通信、PWM,见 9.4 节 FIG 6),加大 \(K_p\) 终究会失稳。控制器输出电压,1 kHz,目标是 1° 的小阶跃(电压远小于 24 V 的电源上限,所以这里不考虑饱和)。

    How do you choose three gains without a model? Ziegler and Nichols (1942) proposed a purely experimental recipe: use P alone and raise \(K_p\) until the system shows a sustained oscillation that neither grows nor decays. That gain is the ultimate gain \(K_u\) and the oscillation period is \(T_u\). The two numbers say "how far from instability, and how fast" the system is, and a table turns them into PID gains.

    The experiment needs a plant that eventually goes unstable as \(K_p\) grows. FIG 1's joint is a second-order system with almost no damping (and no delay): more P only makes it oscillate faster and never makes it unstable, so it is unsuitable. Common in real servos is a voltage-driven DC-motor joint: the motor of Lesson 9.4 (\(k_t=k_e=0.06\), \(R=0.5\ \Omega\), \(J_m=10^{-4}\) kg·m²) drives the same 0.5 kg·m² link through a 50:1 gearbox (horizontal, no gravity). The back-EMF provides strong damping, and with a 10 ms loop delay (filtering, communication, PWM; see FIG 6 of Lesson 9.4) a large enough \(K_p\) does go unstable. The controller outputs voltage at 1 kHz and the target is a small 1° step (the voltage stays far below the 24 V supply, so saturation is left out here).

    FIG 4Ziegler–Nichols 与手动整定Ziegler–Nichols and tuning by hand
    琥珀色是关节角(度),紫色虚线是 1° 的目标,青色细线是电压 \(u\)(右侧刻度,V)。"① 找 \(K_u\)"模式只用 P,画 3 秒,读数给出相隔一个周期的两个峰值之比(大于 1 发散,小于 1 衰减)和测得的周期。"② PID"模式画 1.5 秒;三个 Z–N 按钮按表格填入增益(用本图的 \(K_u\approx299\) V/rad、\(T_u\approx0.136\) s)。 Amber is the joint angle (degrees), the purple dashed line the 1° target, the thin cyan line the voltage \(u\) (right-hand scale, V). Mode "① find \(K_u\)" uses P only over 3 s; the readout gives the ratio of two peaks one period apart (above 1 grows, below 1 decays) and the measured period. Mode "② PID" plots 1.5 s; the three Z–N buttons fill in the gains from the table (using this plant's \(K_u\approx299\) V/rad and \(T_u\approx0.136\) s).
    试一试Try this
    • 在"① 找 \(K_u\)"模式里调 \(K_p\),让峰值比落在 0.97 到 1.03 之间(几乎等幅振荡)。读出 \(K_u\) 和 \(T_u\)。In mode "① find \(K_u\)", tune \(K_p\) until the peak ratio lies between 0.97 and 1.03 (almost sustained oscillation). Read off \(K_u\) and \(T_u\).
    • 切到"② PID",点"Z–N PID":超调约 76%,太猛了。手动调(保留积分 \(K_i>0\)),让超调小于 5% 且调节时间小于 0.25 s。提示:这个对象本身就带一个积分器(电压决定的是速度),需要的 \(K_i\) 很小。Switch to "② PID" and press "Z–N PID": about 76% overshoot, far too aggressive. Tune by hand (keep some integral, \(K_i>0\)) for overshoot below 5% and settling time below 0.25 s. Hint: the plant already contains an integrator (voltage sets speed), so it needs very little \(K_i\).

    采样率:控制器多快才够快Sample rate: how fast is fast enough

    数字控制器每 \(T_s\) 秒才看一眼传感器,中间输出保持不变(零阶保持,2.5 节)。而且"读传感器 → 计算 → 写电机"这一步本身要花时间,很多实现里算出的力矩要到下一个周期才生效,这是一拍延迟。两者合起来大约是 \(1.5\,T_s\) 的延迟。延迟吃掉相位裕度(9.4 节),采样越慢越接近失稳。下图:FIG 1 的关节去掉重力,用连续时间设计好的 PD(\(K_p=50,\ K_d=7\),\(\omega_n=10\) rad/s,\(\zeta\approx0.7\),闭环带宽约 1.6 Hz),目标跳 30°,力矩限 ±30 N·m。

    A digital controller looks at the sensor only every \(T_s\) seconds and holds its output in between (the zero-order hold of Lesson 2.5). Moreover, "read sensor → compute → write motor" takes time, and many implementations apply the computed torque only at the next sample: a one-sample delay. Together that is roughly \(1.5\,T_s\) of delay. Delay eats phase margin (Lesson 9.4), so the slower the sampling, the closer to instability. In the figure, FIG 1's joint without gravity runs a PD designed in continuous time (\(K_p=50,\ K_d=7\), \(\omega_n=10\) rad/s, \(\zeta\approx0.7\), closed-loop bandwidth about 1.6 Hz), the target steps by 30°, and torque is limited to ±30 N·m.

    FIG 5采样率与一拍延迟Sample rate and one-sample delay
    上:琥珀色是采样控制下的关节角,紫色虚线是同一组增益的连续时间控制器(理想参考)。下:青色台阶是采样后送给电机的力矩,每个台阶就是一个采样周期。D 项用测量值的后向差分。 Top: amber is the joint angle under the sampled controller; the purple dashed line is the continuous-time controller with the same gains (the ideal reference). Bottom: the cyan staircase is the sampled torque sent to the motor; each step is one sample period. The D term uses a backward difference of the measurement.
    试一试Try this
    • 保持"一拍延迟",把采样率往下调,找到超调仍小于 10% 的最低采样率(不高于 45 Hz)。它是闭环带宽 1.6 Hz 的多少倍?再往下调一两档,会怎样?With "one-sample delay" on, lower the sample rate to the slowest one that still keeps the overshoot below 10% (at most 45 Hz). How many times the 1.6 Hz closed-loop bandwidth is it? Go one or two notches lower: what happens?
    一个概念,三个视角One idea, three views

    时域视角(FIG 1–3):P、I、D 分别是现在、过去、将来的误差。频域视角(FIG 4–5):P 决定"多快"(穿越频率),D 在穿越频率附近补相位,I 在低频把增益抬到无穷大(所以稳态误差为零),而延迟和采样在高频吃相位;整定就是在这些之间找平衡。极点视角:线性化后的闭环是一个三阶系统,三个增益决定三个极点的位置(第 5 节写出特征多项式;极点配置是 13.2 节的主题)。

    Time-domain view (FIG 1–3): P, I and D act on the present, past and future error. Frequency-domain view (FIG 4–5): P sets "how fast" (the crossover frequency), D adds phase near crossover, I pushes the gain to infinity at low frequency (hence zero steady-state error), and delay and sampling eat phase at high frequency; tuning balances these. Pole view: linearized, the closed loop is a third-order system whose three poles are placed by the three gains (Section 5 writes its characteristic polynomial; pole placement is the subject of Lesson 13.2).

    5再看数学The math

    提示:把鼠标悬停在公式中带底色的符号上,对应的图元素会被高亮(FIG 1 的 P、I、D 条形与曲线,FIG 2 的力矩曲线,FIG 3 的积分曲线,FIG 4 的关节角曲线,FIG 6 的前馈力矩曲线)。Tip: hover the highlighted symbols in the formulas and the matching element lights up (FIG 1's P, I and D bars and curves, FIG 2's torque curve, FIG 3's integral curve, FIG 4's joint-angle curve, FIG 6's feed-forward torque curve).

    稳态误差:为什么 P 停不到目标Steady-state error: why P falls short

    PD 控制(D 取测量值)下,手臂静止时 \(\dot q=\ddot q=0\),运动方程只剩 \(mg\ell\sin q=K_p(q_d-q)\)。所以静止点满足下式(右边是 \(e_{ss}\) 很小时的近似):

    Under PD control (D on the measurement), a resting arm has \(\dot q=\ddot q=0\) and the equation of motion reduces to \(mg\ell\sin q=K_p(q_d-q)\). The resting point therefore satisfies the following (the right-hand side is the approximation for small \(e_{ss}\)):

    $$ K_p\,e_{ss}=mg\ell\,\sin(q_d-e_{ss})\qquad\Longrightarrow\qquad e_{ss}\approx\frac{mg\ell\sin q_d}{K_p+mg\ell\cos q_d} . $$

    FIG 1 的数:\(K_p=20\),\(q_d=60°\),\(e_{ss}=0.156\) rad \(=8.9°\)(近似式给 9.0°);\(K_p=60\) 时 3.2°。误差与 \(K_p\) 成反比,永远不为零。(若关节的负载是常值摩擦或别的常值扰动,结论一样。)

    With FIG 1's numbers, \(K_p=20\) and \(q_d=60°\) give \(e_{ss}=0.156\) rad \(=8.9°\) (the approximation gives 9.0°); \(K_p=60\) gives 3.2°. The error falls as \(1/K_p\) but never reaches zero. (The same holds for any constant load, such as friction or another constant disturbance.)

    积分为什么消除它:闭环的三阶方程Why the integral removes it: the closed loop is third order

    在目标附近线性化:\(q=q_d-e\),\(\sin q\approx\sin q_d-e\cos q_d\),记重力"刚度" \(k_g=mg\ell\cos q_d\)(向下挂着时为正,过了水平线、手臂朝上时为负)。代入 PID(D 取测量值,\(\dot q=-\dot e\)):

    Linearize around the target: \(q=q_d-e\), \(\sin q\approx\sin q_d-e\cos q_d\), and call \(k_g=mg\ell\cos q_d\) the gravity "stiffness" (positive below horizontal, negative once the arm points upward). Insert the PID (D on the measurement, \(\dot q=-\dot e\)):

    $$ J\ddot e+(b+K_d)\,\dot e+(K_p+k_g)\,e+K_i\!\int\! e\,dt\;=\;mg\ell\sin q_d . $$

    右边 \(mg\ell\sin q_d\) 是一个常值负载。对时间求一次导,它就消失了;右边是这个三阶方程的特征多项式:

    The right-hand side \(mg\ell\sin q_d\) is a constant load. Differentiate once in time and it disappears; on the right is the characteristic polynomial of the resulting third-order equation:

    $$ J\,\dddot e+(b+K_d)\,\ddot e+(K_p+k_g)\,\dot e+K_i\,e=0,\quad Js^3+(b+K_d)s^2+(K_p+k_g)s+K_i . $$

    只要闭环稳定,\(e\to0\),不管负载多大:积分把"常值扰动"变成了方程右边求导后消失的东西。三阶多项式稳定(三个根都在左半平面)的充要条件(Routh–Hurwitz)是所有系数为正,并且

    As long as the closed loop is stable, \(e\to0\) whatever the load: the integral turns a "constant disturbance" into something that vanishes when the equation is differentiated. A cubic is stable (all three roots in the left half-plane) if and only if (Routh–Hurwitz) all coefficients are positive and

    $$ (b+K_d)\,(K_p+k_g)\;>\;J\,K_i . $$

    这解释了 FIG 1 的 PI 预设(\(K_p=20,\ K_i=15,\ K_d=0\),\(q_d=60°\),\(k_g=2\)):\(0.1\times22=2.2<0.5\times15=7.5\),不稳定:手臂停不下来,只是因为力矩饱和才被限制在一个持续的大幅摆动里(约 5° 到 85°)。加上 \(K_d=3\):\(3.1\times22=68>7.5\),稳定。积分要靠阻尼来"托住":增大 \(K_i\) 就要有足够的 \(K_d\)(或物理阻尼)。另外 \(q_d\) 越过水平线后 \(k_g<0\),同一组增益的裕度会变小。极点、稳定性与极点配置在 13.2 节系统地讲。

    This explains FIG 1's PI preset (\(K_p=20,\ K_i=15,\ K_d=0\), \(q_d=60°\), \(k_g=2\)): \(0.1\times22=2.2<0.5\times15=7.5\), unstable: the arm never settles, and only torque saturation keeps it in a sustained large swing (roughly 5° to 85°). Add \(K_d=3\): \(3.1\times22=68>7.5\), stable. The integral has to be "held up" by damping: more \(K_i\) needs enough \(K_d\) (or physical damping). Also, past horizontal \(k_g<0\), so the same gains leave less margin. Poles, stability and pole placement are treated systematically in Lesson 13.2.

    离散 PID、滤波微分与抗饱和The discrete PID, the filtered derivative and anti-windup

    工程上常写成"标准形式":\(u=K_p\big(e+\tfrac1{T_i}\int e+T_d\dot e\big)\),积分时间 \(T_i=K_p/K_i\),微分时间 \(T_d=K_d/K_p\)。实际实现的微分是一阶滤波的:

    Practitioners often use the "standard form" \(u=K_p\big(e+\tfrac1{T_i}\int e+T_d\dot e\big)\), with integral time \(T_i=K_p/K_i\) and derivative time \(T_d=K_d/K_p\). The derivative actually implemented is first-order filtered:

    $$ \begin{gathered} D(s)=\hl{Df}{\frac{K_d\,s}{1+s/\omega_f}}\,\big(-Q(s)\big),\qquad \omega_f=2\pi f_c,\\[6pt] d_k=\alpha\,d_{k-1}+(1-\alpha)\,\frac{-(y_k-y_{k-1})}{T_s},\quad \alpha=e^{-\omega_fT_s}. \end{gathered} $$

    右式就是 FIG 2 的代码。测量 \(y_k\) 带独立噪声(标准差 \(\sigma\))时,未滤波的差分噪声标准差是 \(\sqrt2\,\sigma/T_s\):\(\sigma=0.05°=8.7\times10^{-4}\) rad、\(T_s=1\) ms 给出 1.2 rad/s,乘 \(K_d=4\) 就是约 5 N·m 的力矩抖动,与 FIG 2 读数(5.2 N·m,含量化)一致。低通让噪声按 \(f_c\) 下降,但给 D 加了相位滞后:\(f_c\) 接近闭环带宽时,阻尼来得太晚,就会超调(FIG 2 的 \(f_c\le1\) Hz)。对目标跳变 \(\Delta r\),未滤波的"对误差求导"给出一拍的尖峰 \(K_d\Delta r/T_s\):\(4\times1.047/0.001\approx4200\) N·m。

    The right-hand expression is FIG 2's code. If the measurement \(y_k\) carries independent noise of standard deviation \(\sigma\), the unfiltered difference has noise \(\sqrt2\,\sigma/T_s\): \(\sigma=0.05°=8.7\times10^{-4}\) rad and \(T_s=1\) ms give 1.2 rad/s, times \(K_d=4\) about 5 N·m of torque jitter, matching FIG 2's readout (5.2 N·m including quantization). The low-pass cuts noise in proportion to \(f_c\) but adds phase lag to D: when \(f_c\) nears the closed-loop bandwidth the damping arrives too late and the response overshoots (FIG 2 at \(f_c\le1\) Hz). For a target jump \(\Delta r\), the unfiltered derivative of the error gives a one-sample spike \(K_d\Delta r/T_s\): \(4\times1.047/0.001\approx4200\) N·m.

    抗饱和。记控制器想要的输出 \(v\)、执行器实际输出 \(u=\mathrm{sat}(v)\)。(a) 是条件积分,(b) 是反算:

    Anti-windup. Write \(v\) for the output the controller wants and \(u=\mathrm{sat}(v)\) for what the actuator delivers. (a) is clamping, (b) is back-calculation:

    $$ \text{(a)}\ \ \dot I=\begin{cases}0, & u\ne v\ \wedge\ e\,v>0\\ K_i\,e, & u=v\ \vee\ e\,v\le0\end{cases}\qquad\qquad \text{(b)}\ \ \dot I=K_i\,e+\hl{aw}{\tfrac{1}{T_t}\,(u-v)} . $$

    反算在饱和时把 \(I\) 以时间常数 \(T_t\) 拉向"刚好不饱和"的值。\(T_t\) 太大(\(k_t\) 太小)等于没有抗饱和;太小则积分被压得太死,离开饱和后要慢慢爬(FIG 3:\(k_t=10\) 时不再超调,但调节要约 3 s)。Åström 与 Hägglund 建议 \(T_t\approx\sqrt{T_iT_d}\):FIG 3 的 \(T_i=1\) s、\(T_d=1/6\) s 给出 \(T_t\approx0.41\) s,即 \(k_t\approx2.4\),落在 FIG 3 的好区间里:默认的 8 N·m 上限下,"被手挡住"情景中 \(k_t\) 取 1.5 到 2.5 都能完成上面的任务。

    Back-calculation pulls \(I\) toward the "just not saturating" value with time constant \(T_t\) during saturation. Too large a \(T_t\) (too small a \(k_t\)) is no anti-windup at all; too small pins the integral down so hard that it has to creep up afterwards (FIG 3: \(k_t=10\) no longer overshoots, but settling takes about 3 s). Åström and Hägglund suggest \(T_t\approx\sqrt{T_iT_d}\): FIG 3's \(T_i=1\) s and \(T_d=1/6\) s give \(T_t\approx0.41\) s, i.e. \(k_t\approx2.4\), inside FIG 3's good range: with the default 8 N·m limit in the "held by a hand" scenario, any \(k_t\) from 1.5 to 2.5 passes the task above.

    把它们合起来,就是一个可以直接用的控制循环(本页所有图都用这段逻辑):

    Put together, this is a control loop you can use as is (every figure on this page runs this logic):

    // 每 Ts 秒执行一次
    e      = r - y                          // 误差
    P      = Kp * e
    dRaw   = -(y - y_prev) / Ts             // 对测量值求导:目标跳变时没有冲击
    dF     = a * dF + (1 - a) * dRaw        // 一阶低通,a = exp(-2π fc Ts)
    v      = P + I + Kd * dF + u_ff         // 想要的输出(u_ff:前馈)
    u      = clamp(v, -umax, umax)          // 执行器能给的输出
    I     += (Ki * e + (u - v) / Tt) * Ts   // 积分 + 反算抗饱和
    y_prev = y
    send(u)
    // runs every Ts seconds
    e      = r - y                          // error
    P      = Kp * e
    dRaw   = -(y - y_prev) / Ts             // derivative of the measurement: no kick on target jumps
    dF     = a * dF + (1 - a) * dRaw        // first-order low-pass, a = exp(-2π fc Ts)
    v      = P + I + Kd * dF + u_ff         // wanted output (u_ff: feed-forward)
    u      = clamp(v, -umax, umax)          // what the actuator can deliver
    I     += (Ki * e + (u - v) / Tt) * Ts   // integral + back-calculation anti-windup
    y_prev = y
    send(u)

    另一种常见写法是增量式(velocity form):每拍只算增量 \(\Delta u_k=K_p(e_k-e_{k-1})+K_iT_se_k+K_d(e_k-2e_{k-1}+e_{k-2})/T_s\),再令 \(u_k=\mathrm{sat}(u_{k-1}+\Delta u_k)\)。因为累加的是饱和后的 \(u\),它天然不会积分饱和,切换手动/自动时也没有跳变(无扰切换)。还要注意:如果代码里存的是"每拍增益" \(K_iT_s\) 和 \(K_d/T_s\),改变采样周期就会悄悄改变实际的 \(K_i\)、\(K_d\)。用 Tustin(双线性)变换 \(s\leftarrow\tfrac2{T_s}\tfrac{z-1}{z+1}\) 离散化比后向差分更接近连续设计,但在 \(f_s\) 远高于带宽时差别很小。

    Another common form is the velocity (incremental) form: each sample computes only the increment \(\Delta u_k=K_p(e_k-e_{k-1})+K_iT_se_k+K_d(e_k-2e_{k-1}+e_{k-2})/T_s\) and sets \(u_k=\mathrm{sat}(u_{k-1}+\Delta u_k)\). Because it accumulates the saturated \(u\), it cannot wind up, and switching between manual and automatic is bumpless. Note also: if the code stores "per-sample gains" \(K_iT_s\) and \(K_d/T_s\), changing the sample period silently changes the real \(K_i\) and \(K_d\). Discretizing with the Tustin (bilinear) map \(s\leftarrow\tfrac2{T_s}\tfrac{z-1}{z+1}\) stays closer to the continuous design than the backward difference, but the difference is small when \(f_s\) is far above the bandwidth.

    Ziegler–Nichols 表与采样率经验法则The Ziegler–Nichols table and the sample-rate rule of thumb

    Ziegler–Nichols 的"临界增益"表(目标是每个周期振幅衰减到 1/4):

    The Ziegler–Nichols ultimate-gain table (designed for a quarter-amplitude decay per cycle):

    \(K_p\)\(T_i\)\(T_d\)FIG 4 中(\(K_i=K_p/T_i,\ K_d=K_pT_d\))in FIG 4 (\(K_i=K_p/T_i,\ K_d=K_pT_d\))
    P\(0.5\,\hl{Ku}{K_u}\)——\(K_p=149\)
    PI\(0.45\,\hl{Ku}{K_u}\)\(T_u/1.2\)—\(K_p=134,\ K_i=1186\)
    PID\(0.6\,\hl{Ku}{K_u}\)\(T_u/2\)\(T_u/8\)\(K_p=179,\ K_i=2637,\ K_d=3.05\)

    在 FIG 4 的对象上,Z–N PID 的超调是 76%,Z–N PI 在 1.5 s 内都没有调节下来。原因有二:Z–N 本来是为"抑制负载扰动"、按 1/4 衰减设计的,对目标阶跃本来就猛;而这个对象(电压 → 速度 → 位置)自带一个积分器,再加一个强积分就太多了。手动调到 \(K_p=100,\ K_i=10,\ K_d=4\),超调 0.7%,0.18 s 调节完。所以 Z–N 是起点,不是终点。手动整定的常用顺序:先只用 P,加到响应够快、略有超调;再加 D 压住振荡;最后加一点 I 消除稳态误差;若有噪声或执行器抖动,回头降 D 或加滤波。对串级回路,从内环往外调(FIG 7)。

    On FIG 4's plant, Z–N PID overshoots by 76%, and Z–N PI has not settled after 1.5 s. Two reasons: Z–N was designed for rejecting load disturbances with quarter-amplitude decay, which is aggressive for setpoint steps anyway, and this plant (voltage → speed → position) already contains an integrator, so a strong extra integral is too much. Tuned by hand to \(K_p=100,\ K_i=10,\ K_d=4\), the overshoot is 0.7% and it settles in 0.18 s. So Z–N is a starting point, not the answer. A common manual order: P alone, raised until the response is fast with a little overshoot; then D to damp the ringing; finally a little I to remove the steady-state error; if noise or actuator buzz appears, back off D or filter it. For cascaded loops, tune from the inside out (FIG 7).

    FIG 5 的结果给出一个经验法则:带一拍延迟时,40 Hz(闭环带宽 1.6 Hz 的 25 倍)超调 8%,35 Hz 已到 16%,到 25 Hz 及以下就成了持续振荡。教科书常说采样率至少取闭环带宽的 20 倍左右,再留出噪声与延迟的余量;机械臂关节环常用 1 kHz,电流环 10 kHz 量级,正是这个道理。

    FIG 5 gives a rule of thumb: with a one-sample delay, 40 Hz (25 times the 1.6 Hz closed-loop bandwidth) overshoots by 8%, 35 Hz already by 16%, and at 25 Hz and below the loop settles into a sustained oscillation. Textbooks commonly recommend sampling at least about 20 times the closed-loop bandwidth, with extra margin for noise and delay; that is why arm joint loops typically run at 1 kHz and current loops at the 10 kHz scale.

    前馈与串级Feed-forward and cascades

    反馈只能在误差出现之后才纠正。如果知道要走的轨迹 \(q_d(t)\) 和模型,就可以提前把需要的力矩送出去:

    Feedback can only correct an error after it appears. If the trajectory \(q_d(t)\) and a model are known, the needed torque can be sent ahead of time:

    $$ u=\hl{ff}{\underbrace{\hat J\,\ddot q_d+\hat b\,\dot q_d+\widehat{mg\ell}\,\sin q_d}_{u_{ff}}}\;+\;\underbrace{K_p\,e+K_d\,\dot e\,(+K_i\!\textstyle\int e)}_{u_{fb}} . $$

    \(u_{ff}\) 是前馈,\(u_{fb}\) 是反馈。模型精确时,代入运动方程得 \(J\ddot e+(b+K_d)\dot e+K_pe+\dots\approx0\)(在轨迹附近线性化):误差方程不再有"驱动项",不论轨迹多快,误差都只会衰减。模型不准时,驱动项正比于模型误差,反馈只需要处理这部分残差。这和 8.3 节的计算力矩控制是同一个思路(计算力矩还把反馈项乘上模型惯量,并在测得的状态而不是目标轨迹上计算模型项);多关节时 \(u_{ff}\) 由逆动力学(8.2 节的 RNEA)给出。

    Here \(u_{ff}\) is the feed-forward and \(u_{fb}\) the feedback. With an exact model, substituting into the equation of motion gives \(J\ddot e+(b+K_d)\dot e+K_pe+\dots\approx0\) (linearized along the trajectory): the error equation has no "forcing term" any more, so however fast the trajectory, the error only decays. With an imperfect model the forcing is proportional to the model error, and feedback handles only that residual. This is the same idea as the computed-torque control of Lesson 8.3 (which also multiplies the feedback by the model inertia and evaluates the model at the measured state rather than along the desired trajectory); with many joints \(u_{ff}\) comes from inverse dynamics (the RNEA of Lesson 8.2).

    串级把一个难对象拆成一串简单对象,每层一个 P 或 PI。以 FIG 7 的伺服驱动器为例:电流环的对象是绕组 \(1/(Ls+R)\),用 PI 且令零点抵消极点,\(K_p=L\omega_c\)、\(K_i=R\omega_c\),闭环近似 \(\omega_c/(s+\omega_c)\);速度环的对象(电流环看作 1)是 \(k_t/(J_ts)\),PI 取 \(K_p=J_t\omega_v/k_t\),零点放在 \(\omega_v/4\);位置环的对象(速度环看作 1)是积分器,P 增益就是带宽 \(\omega_p\)。"内环看作 1"成立的前提是带宽分离:每层比外层快 4–10 倍。

    A cascade splits a hard plant into a chain of easy ones, with one P or PI per level. For FIG 7's servo drive: the current loop's plant is the winding \(1/(Ls+R)\); a PI whose zero cancels its pole, \(K_p=L\omega_c\), \(K_i=R\omega_c\), gives a closed loop of about \(\omega_c/(s+\omega_c)\). The velocity loop's plant (treating the current loop as 1) is \(k_t/(J_ts)\), with a PI of \(K_p=J_t\omega_v/k_t\) and its zero at \(\omega_v/4\). The position loop's plant (treating the velocity loop as 1) is an integrator, and its P gain is simply the bandwidth \(\omega_p\). "Treat the inner loop as 1" holds only with bandwidth separation: each level 4–10 times faster than the one outside it.

    6机器人中的它:前馈与串级伺服In robotics: feed-forward and cascaded servos

    机械臂很少只是"停在一个角度",它要跟踪一条轨迹。下图让 FIG 1 的关节跟踪 \(q_d(t)=70°+50°\sin(2\pi t/T)\),反馈是固定的 PD(\(K_p=30\),\(K_d=4\),D 取误差导数,因为轨迹是光滑的,不会有冲击)。三个按钮分别打开重力、速度(粘滞摩擦)、加速度(惯量)三项前馈;"模型误差"让控制器以为 \(J,b,mg\ell\) 都是真实值的 \(1+\delta\) 倍。

    An arm rarely just "holds an angle"; it has to track a trajectory. In the figure FIG 1's joint tracks \(q_d(t)=70°+50°\sin(2\pi t/T)\) with a fixed PD (\(K_p=30\), \(K_d=4\), D on the error derivative, since the trajectory is smooth and cannot kick). Three buttons switch on the gravity, velocity (viscous friction) and acceleration (inertia) feed-forward terms; "model error" makes the controller believe \(J,b,mg\ell\) are \(1+\delta\) times the true values.

    FIG 6反馈追,前馈领Feedback chases, feed-forward leads
    关节(按"播放")JOINT · press Play
    右图三栏:(1) 琥珀色 \(q\)、紫色虚线 \(q_d\)(度);(2) 红色跟踪误差 \(e\)(度,量程自动);(3) 绿色是前馈力矩 \(u_{ff}\),青色是反馈力矩 \(u_{fb}\)(N·m)。RMS 误差从 \(t=2\) s 起算(跳过初始过渡)。前馈全开且模型准确时,反馈几乎无事可做,青色线贴近零。 Right, three panels: (1) amber \(q\), purple dashed \(q_d\) (degrees); (2) red tracking error \(e\) (degrees, auto-scaled); (3) green the feed-forward torque \(u_{ff}\), cyan the feedback torque \(u_{fb}\) (N·m). The RMS error counts from \(t=2\) s (skipping the start-up transient). With all feed-forward on and an exact model, feedback has almost nothing left to do and the cyan line hugs zero.
    试一试Try this
    • 周期 3 s 时只开重力前馈,RMS 误差从约 5.8° 降到约 2.7°;周期 2 s 时只降了一点点,为什么?然后打开全部三项前馈,并把模型误差调到 \(|\delta|\ge20\%\):RMS 误差仍小于 1.5°。At a 3 s period, gravity feed-forward alone cuts the RMS error from about 5.8° to about 2.7°; at a 2 s period it barely helps. Why? Then switch on all three terms and set the model error to \(|\delta|\ge20\%\): the RMS error still stays below 1.5°.

    最后看一个真实伺服驱动器内部。位置环(1 kHz)输出速度指令,速度环(5 kHz)输出电流指令,电流环(20 kHz)输出电压。电机沿用 9.4 节(24 V 供电,绕组电感取 0.5 mH),驱动器限流设为 20 A(低于 9.4 节电机 30 A 的峰值),50:1 减速,负载就是 FIG 4 的连杆。目标在 0.05 s 时跳 10°,0.6 s 时有人在关节上加 10 N·m 的负载("负载阶跃")。电流环带宽固定为 1 kHz(\(2\pi\times1000\) rad/s),你可以调位置环带宽 \(\omega_p\) 和速度环与位置环的带宽比。

    Finally, a look inside a real servo drive. The position loop (1 kHz) outputs a speed command, the velocity loop (5 kHz) a current command, the current loop (20 kHz) a voltage. The motor is that of Lesson 9.4 (24 V supply, winding inductance taken as 0.5 mH), the drive limits the current to 20 A (below that motor's 30 A peak), a 50:1 gearbox, and FIG 4's link as the load. The target steps by 10° at 0.05 s, and at 0.6 s someone applies a 10 N·m load to the joint (a "load step"). The current loop's bandwidth is fixed at 1 kHz (\(2\pi\times1000\) rad/s); you set the position-loop bandwidth \(\omega_p\) and the ratio of velocity-loop to position-loop bandwidth.

    FIG 7伺服驱动器的三层串级The three nested loops of a servo drive
    qd→ 位置 Pposition P1 kHz · ωp→ ωref → 速度 PIvelocity PI5 kHz · ωv→ iref → 电流 PIcurrent PI20 kHz · ωc→ V → 电机 + 50:1 + 连杆motor + 50:1 + link
    三栏从上到下对应三层回路:(1) 琥珀色关节角 \(q\)、紫色虚线目标(度);(2) 电机转速:紫色是速度环收到的指令 \(\omega_{ref}\),琥珀色是实际转速(rad/s,电机侧);(3) 电流:青色是电流指令 \(i_{ref}\),琥珀色是实际电流(A),虚线是 ±20 A 限流。默认设置下,阶跃一开始电流就顶到限流:速度环的输出饱和,驱动器以最大力矩加速,这正是串级的另一个好处,限流天然在里面。竖虚线是负载阶跃时刻。 The three panels, top to bottom, match the three loops: (1) amber joint angle \(q\), purple dashed target (degrees); (2) motor speed: purple is the command \(\omega_{ref}\) the velocity loop receives, amber the actual speed (rad/s, motor side); (3) current: cyan is the current command \(i_{ref}\), amber the actual current (A), dashed the ±20 A limit. At the default settings, right after the step the current hits the limit: the velocity loop's output saturates and the drive accelerates at maximum torque, another benefit of the cascade, since current limiting is built in. The vertical dashed line marks the load step.
    试一试Try this
    • 在默认 \(\omega_p=20\) rad/s 下先把带宽比调到 1(速度环和位置环一样快):超调超过 20%。再调 \(\omega_p\) 和带宽比,让阶跃超调小于 1%,且负载阶跃造成的位置下沉小于 0.25°。带宽比至少要多大?At the default \(\omega_p=20\) rad/s, first set the bandwidth ratio to 1 (velocity loop as fast as the position loop): over 20% overshoot. Then adjust \(\omega_p\) and the ratio for a step overshoot below 1% and a position dip below 0.25° under the load step. How large must the ratio be at least?
    真实系统里In real systems

    伺服驱动器与智能舵机:工业伺服和无刷驱动器都是 FIG 7 的串级结构,电流环跑在 10 kHz 量级(9.4 节)。开源的 ODrive 把三层的增益直接暴露为 pos_gain、vel_gain、vel_integrator_gain;Dynamixel X 系列舵机的控制表里有位置环的 P/I/D 增益、速度环的 P/I 增益,以及速度和加速度两项前馈增益(Feedforward 1st/2nd Gain),与 FIG 6 的速度、加速度前馈是同一个思路。无人机:PX4 的多旋翼姿态控制是"角度误差 → P → 角速度指令 → 角速度 PID → 力矩",参数名如 MC_ROLL_P、MC_ROLLRATE_P/I/D;ArduPilot 对应 ATC_ANG_RLL_P 与 ATC_RAT_RLL_P/I/D。内环(角速度)比外环快,与 FIG 7 同理。足式机器人:Unitree 的底层电机指令包含目标位置、目标速度、前馈力矩和 \(K_p\)、\(K_d\),电机按 \(\tau=K_p(q_d-q)+K_d(\dot q_d-\dot q)+\tau_{ff}\) 执行;强化学习策略通常以几十赫兹输出 \(q_d\),关节 PD 以高得多的频率跟踪。ROS 2:ros2_control 的 control_toolbox 提供带积分限幅(抗饱和)选项的 PID;关节轨迹控制器在以速度或力矩为指令接口时,可以在每个关节上闭一个 PID 环,并加上速度前馈。自动整定:Åström 与 Hägglund(1984)的继电器(relay)法用开关控制激出极限环,安全地测出 \(K_u\)、\(T_u\);3D 打印机固件 Marlin 的 M303 命令就用这种继电器振荡来自动整定喷头温度 PID。

    Servo drives and smart servos: industrial servos and brushless drivers share FIG 7's cascade, with current loops at the 10 kHz scale (Lesson 9.4). The open-source ODrive exposes the three loops' gains directly as pos_gain, vel_gain and vel_integrator_gain; the control table of Dynamixel X-series servos has position P/I/D gains, velocity P/I gains and two feed-forward gains for velocity and acceleration (Feedforward 1st/2nd Gain), the same idea as FIG 6's velocity and acceleration feed-forward. Drones: PX4's multicopter attitude control is "angle error → P → rate setpoint → rate PID → torque", with parameters such as MC_ROLL_P and MC_ROLLRATE_P/I/D; ArduPilot's counterparts are ATC_ANG_RLL_P and ATC_RAT_RLL_P/I/D. The inner (rate) loop is faster than the outer one, as in FIG 7. Legged robots: Unitree's low-level motor command carries a target position, target velocity, feed-forward torque, \(K_p\) and \(K_d\), and the motor executes \(\tau=K_p(q_d-q)+K_d(\dot q_d-\dot q)+\tau_{ff}\); reinforcement-learning policies typically output \(q_d\) at tens of hertz while the joint PD tracks it at a much higher rate. ROS 2: ros2_control's control_toolbox provides a PID with integral-clamping (anti-windup) options, and the joint trajectory controller, when it commands velocity or effort, can close a PID loop per joint with a velocity feed-forward. Auto-tuning: the relay method of Åström and Hägglund (1984) uses on-off control to excite a limit cycle and measures \(K_u\) and \(T_u\) safely; the M303 command of the Marlin 3D-printer firmware uses such a relay oscillation to auto-tune the hot-end temperature PID.

    7常见误区Common pitfalls

    误区 1:仿真里调好的 D,上真机就抖Pitfall 1: a D gain tuned in simulation buzzes on hardware

    仿真里的速度是"干净"的,真机的速度来自编码器差分,量化和噪声被 \(1/T_s\) 放大(FIG 2 里 \(\sigma=0.05°\) 就带来 5 N·m 的力矩抖动)。症状是电机发热、发出啸叫、减速器磨损。对策:D 取测量值,加低通(截止频率取闭环带宽的几倍到十几倍),能直接测速度(测速电机、驱动器内的观测器)就别差分;仿真时把编码器量化和噪声也加进去。

    Simulated velocity is "clean"; on hardware it comes from differencing an encoder, and quantization and noise are amplified by \(1/T_s\) (in FIG 2, \(\sigma=0.05°\) already causes 5 N·m of torque jitter). Symptoms: hot motors, a whining sound, worn gearboxes. Cures: take D on the measurement, low-pass it (cutoff a few to a dozen times the closed-loop bandwidth), use a measured velocity (tachometer, the drive's observer) instead of differencing when you can, and put encoder quantization and noise into the simulation.

    误区 2:忘了积分在"后台"一直在累加Pitfall 2: forgetting the integral keeps accumulating in the background

    手臂碰到限位、被人抓住、夹爪夹住物体、急停后电机断使能:误差还在,积分照样累加。重新使能或松开的那一刻,积好的力矩一下子释放(FIG 3 里积分涨到上限的 10 倍,手臂甩过头顶)。除了抗饱和,还要在控制器失能时冻结或清零积分,切换模式时做无扰切换。接触任务(13.5 节)里常干脆不用积分,或给它很小的限幅。

    The arm hits a joint limit, a person grabs it, the gripper closes on an object, the motors are disabled after an emergency stop: the error persists and the integral keeps growing. At the moment of re-enabling or releasing, the stored torque comes out all at once (in FIG 3 the integral reaches 10 times the limit and the arm swings over the top). Besides anti-windup, freeze or reset the integral while the controller is disabled and switch modes bumplessly. Contact tasks (Lesson 13.5) often drop the integral altogether or give it a tight clamp.

    误区 3:用积分去干前馈的活Pitfall 3: making the integral do feed-forward's job

    重力矩随姿态变化,而积分是慢的:每次姿态改变,积分都要重新"找"一遍,期间就有误差和超调。已知的力(重力、粘滞摩擦、惯性力)应该用前馈直接给出(FIG 6:周期 3 s 时 RMS 误差从 5.8° 降到 0.002°),积分只留给真正未知的、慢变的残差。增大 \(K_i\) 去追轨迹,换来的往往是振荡(第 5 节的稳定条件)。

    Gravity torque changes with posture while the integral is slow: every posture change makes it "search" again, with error and overshoot meanwhile. Known forces (gravity, viscous friction, inertia) belong in feed-forward (FIG 6: at a 3 s period the RMS error drops from 5.8° to 0.002°), and the integral should be left only the truly unknown, slowly varying residual. Raising \(K_i\) to chase a trajectory usually buys oscillation (the stability condition of Section 5).

    误区 4:串级回路从外往里调,或者内外一样快Pitfall 4: tuning a cascade outside-in, or with inner and outer loops equally fast

    外环的设计假设"内环像一根导线"(增益为 1、没有延迟)。内环没调好,外环的增益就是在补偿内环的毛病;内外带宽太接近,两层的动态耦合,超调和振荡就来了(FIG 7 默认 \(\omega_p=20\) rad/s、带宽比为 1 时超调超过 20%)。正确顺序是先电流环、再速度环、最后位置环,每层比外一层快 4–10 倍。无人机同理:先调角速度环,再调角度环。

    The outer loop is designed assuming "the inner loop is a wire" (gain 1, no delay). If the inner loop is badly tuned, the outer gains end up compensating its flaws; if inner and outer bandwidths are too close, the two dynamics couple and overshoot and ringing appear (FIG 7 overshoots by more than 20% at ratio 1 with the default \(\omega_p=20\) rad/s). The right order is current, then velocity, then position, each 4–10 times faster than the next one out. Drones are the same: tune the rate loop first, then the angle loop.

    误区 5:以为增益越大越好Pitfall 5: believing more gain is always better

    大 \(K_p\) 让稳态误差变小、响应变快,但每个真实回路都有延迟(采样、滤波、通信、PWM)。一段延迟 \(\Delta\) 在穿越频率 \(\omega_{gc}\) 处吃掉 \(\omega_{gc}\Delta\) 弧度的相位(9.4 节),增益加到某处必然失稳(FIG 4 的 \(K_u\approx299\) V/rad 就是这个边界)。噪声也随增益放大,执行器也会饱和。好的控制器是在"够快"和"够稳、够安静"之间的平衡,而不是增益的竞赛;想要又快又准,靠的是前馈和更好的模型,不是更大的反馈增益。

    A large \(K_p\) shrinks the steady-state error and speeds up the response, but every real loop has delay (sampling, filtering, communication, PWM). A delay \(\Delta\) eats \(\omega_{gc}\Delta\) radians of phase at the crossover frequency \(\omega_{gc}\) (Lesson 9.4), so at some gain the loop must go unstable (FIG 4's \(K_u\approx299\) V/rad is exactly that boundary). Noise is amplified with the gain, and actuators saturate. A good controller balances "fast enough" against "stable and quiet enough"; it is not a gain contest. Speed and accuracy come from feed-forward and better models, not from ever larger feedback gains.

    8小练习Check yourself

    1. FIG 1 的关节(\(mg\ell=4\) N·m)只用 PD,\(K_p=20\) N·m/rad,目标 \(q_d=90°\)(水平)。手臂停下后,稳态误差大约是多少?1. FIG 1's joint (\(mg\ell=4\) N·m) under PD only, \(K_p=20\) N·m/rad, target \(q_d=90°\) (horizontal). Once the arm comes to rest, what is the steady-state error, roughly?
    静止时 \(K_pe=mg\ell\sin(q_d-e)=4\cos e\)。水平处 \(k_g=mg\ell\cos90°=0\),近似 \(e\approx4/20=0.2\) rad \(\approx11.5°\);精确解 \(20e=4\cos e\) 给出 0.196 rad \(\approx11.2°\)。所以是选项 C。D 项在静止时为零,帮不上忙。At rest \(K_pe=mg\ell\sin(q_d-e)=4\cos e\). At horizontal \(k_g=mg\ell\cos90°=0\), so \(e\approx4/20=0.2\) rad \(\approx11.5°\); the exact solution of \(20e=4\cos e\) is 0.196 rad \(\approx11.2°\). Hence option C. The D term is zero at rest and cannot help.
    2. 为什么加了积分项之后,常值负载下的稳态误差变成了零?2. Why does adding the integral term make the steady-state error under a constant load exactly zero?
    选项 B。平衡时所有状态不变,其中积分状态的导数就是 \(e\),所以必须 \(e=0\)。前提是闭环稳定(第 5 节的 Routh 条件)。选项 A 只是减小误差而不能消除;选项 C 描述的是微分项;积分让系统的阶数升高一阶,而不是变成一阶。Option B. At an equilibrium every state is constant, and the integral state's derivative is \(e\), so \(e=0\) is forced. This presumes a stable closed loop (the Routh condition of Section 5). Option A would only shrink the error, not remove it; option C describes the derivative; the integral raises the system order by one rather than making it first order.
    3. 机械臂每次收到新的目标点,电机都会"咯噔"一下,电流尖峰触发了驱动器过流保护。增益本身调得没问题。最可能的原因和对策是?3. Every time the arm receives a new target point, the motor gives a jolt and a current spike trips the drive's over-current protection. The gains themselves are fine. The most likely cause and fix?
    选项 A。目标在一个采样内跳 \(\Delta r\),\(K_d\Delta r/T_s\) 是巨大的尖峰(FIG 2 里约 4200 N·m)。对测量值求导时目标跳变不进入 D,P 项的跳变 \(K_p\Delta r\) 还在,所以实际中还会把目标轨迹做平滑。加大 \(K_i\)、降采样率都与此无关,后者还会损害稳定性(FIG 5)。Option A. A target jump \(\Delta r\) within one sample makes \(K_d\Delta r/T_s\) a huge spike (about 4200 N·m in FIG 2). Differentiating the measurement keeps target jumps out of D; the P term's jump \(K_p\Delta r\) remains, which is why the target trajectory is also smoothed in practice. More \(K_i\) or a slower loop are unrelated, and the latter hurts stability (FIG 5).
    4. 一个协作臂被人用手挡住几秒,松手后猛地冲过目标很远。下列哪个改动最直接地解决这个问题?4. A cobot is held back by a person for a few seconds; on release it lunges far past the target. Which change addresses this most directly?
    选项 D。被挡住期间误差持续存在,力矩早已顶到上限,积分却还在累加(FIG 3 中涨到上限的约 10 倍)。松手后这笔"欠账"要靠反向误差慢慢还,于是冲过头。抗饱和直接阻止了这件事。加大 \(K_d\) 只是部分压住冲势;提高上限只是把问题推后(被挡住时同样会累积),而且让碰撞更危险。Option D. While held, the error persists, the torque has long hit its limit, yet the integral keeps accumulating (to about 10 times the limit in FIG 3). After release that "debt" is paid back by opposite error: the lunge. Anti-windup stops this directly. More \(K_d\) only partly restrains the lunge; a higher limit merely postpones the problem (the integral still grows while held) and makes collisions more dangerous.
    5. 一个伺服的速度环带宽是 30 rad/s,有人把位置环带宽调到了 25 rad/s,结果超调很大。最合理的做法是?5. A servo's velocity loop has a bandwidth of 30 rad/s, and someone has set the position loop to 25 rad/s; the result overshoots badly. What is the most sensible fix?
    选项 B。外环设计假设内环"像一根导线",这需要带宽分离。带宽比约 1.2 时两层动态强烈耦合(FIG 7 在默认 \(\omega_p\) 下带宽比为 1 时超调超过 20%)。选项 C 让问题更糟;给外环加积分只会再添相位滞后;采样率不是瓶颈。Option B. The outer design assumes the inner loop "is a wire", which needs bandwidth separation. At a ratio of about 1.2 the two dynamics couple strongly (FIG 7 overshoots by more than 20% at ratio 1 with the default \(\omega_p\)). Option C makes it worse; an outer integral only adds phase lag; the sample rate is not the bottleneck.

    9延伸阅读Further reading

    • K. J. Åström & R. M. Murray, Feedback Systems: An Introduction for Scientists and Engineers, Princeton University Press, 2008 (2nd ed. 2021) — PID 一章:积分作用、抗饱和、滤波微分与整定;全书免费在线The PID chapter: integral action, anti-windup, filtered derivative and tuning; the whole book is free online
    • K. J. Åström & T. Hägglund, PID Controllers: Theory, Design, and Tuning, 2nd ed., ISA, 1995; Advanced PID Control, ISA, 2006 — PID 的权威专著:实现细节、抗饱和、自动整定The standard monographs on PID: implementation details, anti-windup, auto-tuning
    • J. G. Ziegler & N. B. Nichols, Optimum Settings for Automatic Controllers, Trans. ASME 64, 1942 — 临界增益法的原始论文The original paper of the ultimate-gain method
    • K. J. Åström & T. Hägglund, Automatic Tuning of Simple Regulators with Specifications on Phase and Amplitude Margins, Automatica 20(5), 1984 — 继电器自动整定Relay auto-tuning
    • N. Minorsky, Directional Stability of Automatically Steered Bodies, J. American Society of Naval Engineers 34, 1922 — 为船舶自动操舵写下的三项控制律,PID 的早期理论分析The three-term control law written down for ship autopilots, an early theoretical analysis of PID
    • G. F. Franklin, J. D. Powell & M. L. Workman, Digital Control of Dynamic Systems, 3rd ed., Addison-Wesley, 1998 — 离散化、采样率选择与零阶保持Discretization, sample-rate selection and the zero-order hold
    • M. W. Spong, S. Hutchinson & M. Vidyasagar, Robot Modeling and Control, 2nd ed., Wiley, 2020 — 独立关节控制:PID、前馈与执行器动力学Independent joint control: PID, feed-forward and actuator dynamics
    • K. Lynch & F. Park, Modern Robotics, Ch. 11 — 机器人控制:误差动力学、PID 与前馈加反馈Robot control: error dynamics, PID, and feed-forward plus feedback
    • B. Beauregard, Improving the Beginner's PID, blog series, 2011 — 从最简 PID 一步步加上采样时间、微分冲击、抗饱和与无扰切换的实践教程A practical walk from the naive PID to sample time, derivative kick, anti-windup and bumpless transfer