1. 执行器与 PID 控制
本章目标
- 理解位置误差、速度误差、P 增益、D 增益各自在干什么
- 能在 MuJoCo 里把一个 hinge 关节从任意初始角度稳定控制到目标角度
- 能画出一张关节角随时间的响应曲线,并解释过冲 / 欠阻尼 / 稳态误差
- 知道为什么工业上几乎所有执行器最低层都是 PD / PID
前置阅读
- 仿真与可视化 · MuJoCo 快速上手
- ROS · 运动学与坐标变换
- 想先有个感性认识,可以直接打开交互页 PD/PID Playground,拖拖滑块再回来看公式。
1.1 执行器
在机器人技术中,执行器(Actuator) 可以被直观地理解为机器人的“肌肉”,它的核心作用是将某种形式的能量(通常是电能、液压能或气压能)转化为机械运动,从而使机器人能够移动自身(如行走、行驶)或与其周围的环境发生物理交互(如抓取、举起、推拉)。
1.1.1 执行器分类
按能量来源,常见执行器可以分成三大类:
| 类别 | 代表 | 典型场景 | 特点 |
|---|---|---|---|
| 电动 | 有刷直流、无刷直流(BLDC)、步进 | 机械臂、足式机器人、无人机 | 控制精度高、响应快,具身智能场景里的绝对主流 |
| 液压 | Boston Dynamics Atlas(老款) | 重载、高爆发 | 功率密度最高,但贵、漏油、噪声大 |
| 气动 | 软体抓手、McKibben 人工肌肉 | 柔性操作、仿生 | 轻、便宜、本征柔顺,但控制精度差 |

电动执行器是目前机器人(尤其是消费级、协作级和大多数工业机器人)中最常见的执行器类型,其中又分出两条明显的路线,对之后调 PID 的影响完全不同:
- 高减速比 + 小电机:谐波减速器 + 伺服电机,典型是 UR、Franka 这类协作机械臂。刚性好、精度高,但几乎不可反向驱动(back-drivable 近乎为零),你用手推不动它的关节。
- 低减速比 + 大电机(准直驱,Quasi-Direct Drive,QDD):MIT Mini Cheetah、Unitree A1/Go2 走的路线。减速比通常只有 6:1 ~ 10:1,电机本体扭矩做得很大,好处是能反向驱动、能靠电流估力矩、落地冲击不容易打坏齿轮。足式机器人基本都选这条路。
1.1.2 衡量执行器的关键指标
这块的指标通常分成两类视角:
- 能力侧:挑硬件、写产品介绍时先看的——回答的是"这块执行器能干什么"。
- 控制侧:写控制回路、调 PD / PID 时反复用到的——回答的是"我要建一个什么样的二阶系统"。
① 能力侧
| 指标 | 符号 / 单位 | 说明 |
|---|---|---|
| 扭矩 / 力 | 机械臂能举多重、机器狗能扛多大冲击 | |
| 速度 | 执行指令的快慢,决定工作效率与动态响应 | |
| 精度 / 重复定位精度 | Precision / Repeatability (mm, °) | 每次到同一个目标点的误差有多大,手术 / 精密装配机器人的核心指标 |
| 反向可驱动性 | back-drivability | 外力能否推着执行器倒转,人机协作安全与力控(Force Control)的基础 |
② 控制侧
| 指标 | 符号 / 单位 | 说明 |
|---|---|---|
| 力矩-转速曲线 | — | 同一时刻扭矩和转速只能二选一,不是两个独立的最大值 |
| 力矩密度 | N·m / kg | 轻量化能力,四足 / 人形的核心指标 |
| 反射惯量 | 减速比 | |
| 编码器分辨率 | counts/rev | |
| 传动效率 | 有多少电能真变成了有用功 | |
| 控制带宽 | Hz | 最高可以跟踪多快的目标变化,决定 |
具身智能项目里最常见的问题,几乎都出在控制侧——而且多半不是数值标错,而是仿真与真机对不上。下面这几条基本覆盖了"仿真可运行、真机无法稳定"的主要成因,也是后续调 PID 时会反复遇到的坑:
- 反射惯量最容易被遗漏。公式里的惯量
不是连杆本身的几何惯量,而是反射到关节输出端的等效惯量——减速比 会将电机转子惯量放大 倍。若 URDF / MJCF 只填连杆、忽略电机转子,仿真中的关节就会明显比真机偏轻。MuJoCo 专门提供了 armature字段来建模这一项,若被忽略,会估得偏高,仿真中调好的 迁移到真机后通常立刻出现高频振荡。 - 力矩饱和并非一次性的硬墙。仿真里
ctrlrange是干净的硬截断,真机上同时叠加三层限制:热限制、电流环响应、反电动势。长时间运行在峰值附近,温度升高会使可持续力矩逐步衰减;当电机转速接近空载转速时,可用力矩也会下降(力矩-转速曲线并非矩形)。仿真里 能稳定运行,前提是默认"需要多少力矩就能输出多少"。 - 传动间隙 / 摩擦默认为零。MJCF 不会自动引入回程间隙(backlash),粘滞摩擦需通过
frictionloss显式建模,库仑 / 静摩擦基本只能手动叠加。真机关节在低速过零、方向反转的瞬间常出现明显顿挫,正是这部分未建模的直接后果——这也是为什么仿真中接近临界阻尼的响应曲线,到真机往往在目标附近残留一层微振。 - 控制回路的延迟在仿真中通常也视作零。真机上"读编码器 → 计算 PD → 发往驱动器 → 实际出力"一个周期 1 ~ 5 ms 非常常见,分布式总线还会更大。该延迟会直接削弱
贡献的阻尼,表现为仿真中接近临界阻尼的响应,到真机呈现轻微欠阻尼。
因此调参时建议遵循一条通用原则:仿真中宁可保守一档,给上述四项 gap 留出余量。迁移到真机时,通常是先下调
1.1.3 齿轮箱
齿轮箱(Gearbox),在机器人和机械工程领域通常被称为减速器(Reducer)或传动装置,你可以把它直观地理解为机器人的**“变速自行车齿轮系统”**。
它的核心功能是:连接在动力源(如电机)和执行端(如机械臂关节、车轮)之间,通过内部不同大小齿轮的物理啮合,来改变旋转的“速度”和“扭力(扭矩)”。
机器人之所以几乎离不开它,是因为 BLDC 等常见电机"转得快、力矩小"(几千 RPM、零点几 N·m),而关节需要的是"慢而有力"(十几 rad/s、几 N·m 到几十 N·m),中间必须经过一级换算:

一套减速比为
- 力矩放大
倍 - 转速缩小
倍 - 电机转子惯量在关节端被放大
倍(最常被忽略,却直接影响 PD 调参和冲击时的受力)
衡量一个齿轮箱时最常关注的几项参数:
| 参数 | 典型单位 | 说明 |
|---|---|---|
| 减速比 | — | 输入转速与输出转速之比,决定力矩 / 速度 / 反射惯量的放大倍数 |
| 额定 / 峰值输出扭矩 | N·m | 输出端的可持续与瞬时承载,超过峰值可能打齿甚至损坏 |
| 传动效率 | % | 输出功率 / 输入功率,行星约 90%、谐波约 70 ~ 80%、摆线约 85 ~ 90% |
| 回程间隙(backlash) | 弧分(arcmin) | 反向运动时的空程,直接决定定位精度与力控质量 |
| 扭转刚度 | N·m / rad | 输出端到达目标位置后,被外力偏转的角度——越大越"硬" |
| 噪声 / 寿命 | dB / 小时 | 协作场景与长期运行中容易被忽视的隐性门槛 |
换算到 PD 调参的语言,可以记住三条结论:
越大,关节越"重":反射惯量按 放大,对应的固有频率 更低,相同 下响应更慢。 越大,反向可驱动性越差:摩擦力矩也被放大,外力几乎推不动电机端,这是协作臂用谐波减速器的直接代价。- 回程间隙和扭转刚度决定位置精度的上限:即便 PD 参数调得再准,齿轮箱自身的空程和柔性也会把最小可达误差钉死在某一档。
机器人上最常见的三类齿轮箱:
| 类型 | 典型减速比 | 回程间隙(backlash) | 常见场景 |
|---|---|---|---|
| 行星齿轮(Planetary) | 3:1 ~ 100:1 | 有(几弧分到几十弧分) | 足式 QDD、低成本机械臂 |
| 谐波减速器(Harmonic Drive) | 30:1 ~ 160:1 | 近似为零 | 协作机械臂、精密装配 |
| 摆线针轮(Cycloidal / RV) | 30:1 ~ 260:1 | 很小 | 工业六轴基座、重载关节 |
三者的取舍大致可以浓缩成:
- 要便宜、要反向可驱动、能接受一点抖动 → 低减速比行星齿轮 + 大电机(QDD 路线,Mini Cheetah / Unitree)
- 要高精度、不在乎反向驱动 → 谐波减速器(UR / Franka 路线)
- 要扛大负载、要刚性 → 摆线针轮(ABB / FANUC 这类工业机械臂基座)
就本章而言,记住一条就够:齿轮箱以不同比例改写了"力矩 / 速度 / 惯量"这三个量,PD 真正控的是改写之后的那个二阶系统。后面在 MuJoCo 里调
1.2 Bang-Bang 控制
正式进入 PID 之前,先看最粗暴的那一类反馈控制——Bang-Bang 控制(又称双位控制或开关控制)。它几乎不需要任何计算,却能帮我们建立起对"反馈"最朴素的直觉:既然要让
1. 核心逻辑:只看误差符号
Bang-Bang 不关心误差多大,只关心误差在哪一侧:
翻译成人话:关节在目标左侧就全力右拉,在目标右侧就全力左拉,中间没有"拉多大"的档位。类比开车,油门和刹车各有一个踏板——要么踩到底,要么完全松开,不存在轻踩。
2. 为什么叫 "Bang-Bang"
每当系统穿越平衡点,执行器会在极短时间内从
3. 致命缺点:极限环与抖动
Bang-Bang 的响应速度极快——它总是出全力。但在机器人关节控制里几乎不会单独使用,原因有三:
- 极限环(Limit Cycle):电机有惯性,全速冲向目标后必然越过零点,算法立刻命令反向全力,结果关节在目标附近永久震荡,静不下来。在相平面
上表现为一条环绕目标点的闭合曲线,数学上称为极限环——它不是"还没收敛",而是理论上永远不会收敛。 - 硬件损耗:电流在
之间高频切换,对驱动器的 H 桥、齿轮箱的换向齿面都是明显冲击,直接缩短寿命。 - 能量浪费:即使离目标只差 0.01°,输出也是全功率,几乎没有节能余地。
4. 两种常见改进
- 引入死区(Deadband):在目标附近设一个宽度为
的小窗口,误差落入就直接关掉动力。可以把极限环压成近乎静止,代价是牺牲 以内的定位精度。 - 用线性区代替开关:既然"离得近就应该出力小",不如让
与 成比例——这就是下一节要讲的比例控制(P)。反过来看,Bang-Bang 恰好是 P 控制在 、并被力矩饱和硬切到 后的退化形式。
5. 什么时候 Bang-Bang 反而是正确答案
虽然机器人关节不用它,但在被控对象本身变化缓慢、执行器天然只有两个状态的场合,Bang-Bang 往往就是最优解:
| 场景 | 执行器 | 为什么合适 |
|---|---|---|
| 家用空调 / 暖气 | 压缩机开 / 关 | 温度变化以分钟计,压缩机无法"开一半" |
| 电热水器 | 加热管通 / 断 | 加热本身是积分型过程,对开关时刻不敏感 |
| 卫星姿态冷气推进 | 喷气阀开 / 关 | 推进器没有连续调节能力 |
一句话结论:当被控对象的时间尺度远大于切换抖动的时间尺度时,Bang-Bang 就是合适的;一旦时间尺度反过来(例如高速关节),就必须升级到 P / PD / PID。
1.3 PID 控制
PID 控制器(Proportional-Integral-Derivative Controller)是工程和自动化领域最经典、应用最广泛的反馈控制算法。具身智能和机器人仿真里,无论是让机械臂关节准确到达并保持某个角度,还是让无人机悬停在指定高度,底层几乎都会用到它——核心思想是根据当前误差、过去的误差累积以及误差的变化趋势,计算出一个控制量,驱动系统靠近目标。
PID 由三部分组成,分别对应时间的三个维度:
1. 比例(Proportional, P) —— "着眼现在"
比例项直接与当前误差成正比,误差越大,输出的控制力就越大。
- 直观感受:目标高度 10 米、无人机当前在 2 米时,差距 8 米,P 控制会输出一个很大的上升油门;随着越来越接近 10 米,误差减小,油门也随之减小。
- 问题:仅靠 P 很难正好到达目标。当无人机非常接近 10 米时,误差已经很小,P 给出的上升力可能刚好等于重力,结果无人机会稳定悬停在 9.5 米左右,永远差一截——这就是稳态误差(Steady-state error)。
2. 积分(Integral, I) —— "回顾过去"
积分项会随时间不断累积过去的误差。
- 直观感受:如果无人机一直卡在 9.5 米,虽然每一瞬间的误差只有 0.5 米,但随着时间推移,积分项会把这 0.5 米不断叠加,累积出越来越大的控制力,最终推着无人机克服残余的重力/阻力,精确到达 10 米。
- 作用:消除稳态误差。
3. 微分(Derivative, D) —— "预测未来"
微分项关注的是误差的变化率(斜率),相当于一个阻尼器,用来预测系统的未来走向。
- 直观感受:当无人机在 P 与 I 的作用下快速冲向 10 米时,惯性很容易让它冲过头——飞到 11 米再掉下来,形成剧烈的振荡(过冲,Overshoot)。D 控制会察觉到误差正在快速缩小,提前"踩刹车",产生一个反向的阻力,让无人机平稳停在 10 米。
- 作用:减少振荡、提高系统稳定性。
三项合起来,标准的 PID 表达式非常优美,把"过去、现在、未来"三个时间维度统一在了一起:
1.4 PD 控制
在工业与机器人控制里,真正启用积分项的场景其实并不多,绝大多数伺服回路最终留下的都是 PD。原因可以归纳成三条:
1. 积分饱和(Integral Windup)在物理世界里会带来灾难性后果
纯数学模型里积分项可以正常工作,但物理世界里执行器的输出力矩是有限的,且环境中到处是障碍物。设想一个关节目标角度是 90°,在 45° 时被桌面卡住:
- 使用 PD 时,关节会保持一个恒定推力(P 项相当于一根被拉伸的弹簧),安全地顶住障碍物。
- 使用 PID 时,只要误差一直存在,积分项就会持续累加,电机收到越来越大的力矩指令,直到硬件上限。一旦障碍物突然移开,累积的积分项瞬间释放,关节会以非常剧烈的姿态甩出去——既容易损坏机器人,也带来明显的人身安全风险。
2. PD 在数学上等效于一组"虚拟弹簧-阻尼器"
现代机器人控制非常看重柔顺性(Compliance)与物理交互的安全,而 PD 律可以严格等效为一个弹簧-阻尼系统:
对应虚拟弹簧的刚度(stiffness),越大关节越"硬"; 对应阻尼器的阻尼系数(damping),越大运动中的阻力越大、越不容易振荡。
这种物理直觉让 PD 天然契合阻抗控制(Impedance Control)——希望机器人像人手一样柔软地抓取物体时,只需调低
3. 重力补偿与前馈控制取代了积分项的角色
I 项在经典控制里的主要职责是消除稳态误差,而机械臂最主要的稳态误差来源正是重力(哪怕目标角度已经近在咫尺,重力也会一直把关节往下拉,纯 P 控制永远差一截)。现代机器人学并不依靠盲目累加误差来对抗重力,而是直接利用动力学模型(Dynamics Model)精确计算,这就是计算力矩控制(Computed Torque Control),在 PD 的基础上加上一层动力学前馈:
是重力补偿项,根据当前位姿直接算出抵消重力所需的力矩; 是惯性矩阵与科氏力前馈。
既然稳态误差已经被精确的物理模型吃掉,积分项就变得可有可无,甚至只会额外引入相位滞后与不稳定性。
因此本章的主线就是 PD:先把 P 与 D 两项的物理含义讲清楚,再在 1.5.4 节通过一次"施加恒定外扰"的实验,直观观察 PD 为什么吃不掉稳态误差——这正是 I 项存在的动机。PID 的完整实现与抗饱和处理,会放到后续真正用得上的章节(例如机械臂力控、底盘轨迹跟踪)再展开。
1.4.1 为什么控制论最先讲 PD
机器人每一个能动的关节,最终都要回答一个最朴素的问题:
"我现在的角度
和目标角度 差多少?我应该输出多大的力矩 去追?"
PD 控制给出的回答就是上一节的公式
之所以把 PD 放在第一章,有三个原因:
- 它是真机与仿真的共同最底层。Stanford Pupper、Unitree Go2、大多数机械臂,伺服驱动器里最内层一定有一条 PD/PID。我们后面用的 MuJoCo
position执行器内部就是一条 PD。 - 它把"反馈"这件事说到最简。以后讲 MPC、RL、Diffusion Policy,本质都在找更复杂的
;PD 是所有反馈策略的退化形式。 - 它可以徒手推导 + 徒手调参。在没有学习、没有优化器的前提下,你依然能把一个关节控得像模像样,这是其它方法很难给的"踏实感"。
1.4.2 从"弹簧-阻尼器"看 PD 的物理直觉
把 PD 律代回单关节的动力学方程(惯量
重新整理一下,这其实就是一个二阶线性系统:
和高中物理里"弹簧-阻尼器"的方程一模一样:
就是弹簧刚度——把关节往 拉回去,越大越"硬"。 就是阻尼系数——消耗动能,越大越"黏"。
由此可以直接写出两个控制工程里绕不开的量:
| 行为 | 直观 | |
|---|---|---|
| 欠阻尼(underdamped) | 冲过目标来回晃 | |
| 临界阻尼(critical) | 最快、不过冲 | |
| 过阻尼(overdamped) | 不晃,但慢吞吞 |
调参的朴素做法:先把
1.5 在 MuJoCo 里实现 PD
理论部分到此为止,下面把公式 position 执行器看整体效果,再自己手搓 PD,把
1.5.1 MuJoCo 里的执行器模型
MuJoCo 用 <actuator> 标签把控制信号 ctrl[i] 翻译成作用在关节上的广义力。对单关节 PD,有三种常见写法:
| 标签 | ctrl[i] 的含义 | 内部在算什么 |
|---|---|---|
<motor> | 直接的关节力矩 | 不做反馈,你自己在 Python 里写 PD |
<position kp=.. kv=..> | 目标位置 | 内部自动算 |
<velocity kv=..> | 目标速度 | 内部算 |
- 如果你想看清 PD 每一项、方便画曲线,用
motor+ 自己写反馈。 - 如果你只是想让关节稳定跟踪到某个角度,用
position(写起来最短,也是 Pupper / 机械臂最常见的配置)。 velocity在轮式底盘、转速环里更常见。
本章我们两种都写一遍:先用 position 看效果,再用 motor 自己手搓 PD,把
1.5.2 第一个 MJCF:一根单摆 + 一个 PD
把下面这段存成 pendulum.xml——一根 0.5 m 长的杆子通过 hinge 挂在世界坐标系上,绕
<mujoco model="single_pendulum">
<option gravity="0 0 -9.81" timestep="0.002"/>
<worldbody>
<body name="link" pos="0 0 1">
<joint name="hinge" type="hinge" axis="0 1 0" damping="0.02"/>
<geom type="capsule" fromto="0 0 0 0 0 -0.5" size="0.04" mass="1"/>
</body>
</worldbody>
<actuator>
<!-- 写法 A:让 MuJoCo 帮你实现 PD -->
<position name="pos_act" joint="hinge" kp="20" kv="1" ctrlrange="-3.14 3.14"/>
<!-- 写法 B:自己在 Python 里手搓 PD -->
<motor name="tor_act" joint="hinge" ctrlrange="-5 5"/>
</actuator>
</mujoco>
对应的最小 Python 脚本 pd_single_joint.py:
import mujoco
import numpy as np
model = mujoco.MjModel.from_xml_path('pendulum.xml')
data = mujoco.MjData(model)
q_des = 0.8 # 目标角度(rad),约 45.8°
Kp, Kd = 20.0, 1.0 # 与写法 A 的 kp/kv 一致,方便对照
use_motor = True # True: 用 motor 手搓 PD; False: 用 position 执行器
log = []
for _ in range(4000): # 4000 * 0.002s = 8s
q = data.qpos[0]
dq = data.qvel[0]
if use_motor:
tau = Kp * (q_des - q) + Kd * (0.0 - dq)
data.ctrl[1] = np.clip(tau, -5.0, 5.0) # motor 通道
else:
data.ctrl[0] = q_des # position 通道
mujoco.mj_step(model, data)
log.append((data.time, q, dq, data.ctrl.copy()))
运行完把 log 里的
:::tip 想立刻看效果?
不想装 MuJoCo 可以直接打开 PD/PID Playground,同一套
1.5.3 实验 1:不同 下的响应曲线
把上面脚本里的 Kp, Kd 改成下面三组,分别录响应曲线,贴到组队群里:
| 组 | 预期 | 现象 | ||
|---|---|---|---|---|
| A 欠阻尼 | 40 | 0.5 | ≈ 0.04 | 反复冲过目标来回晃 |
| B 临界附近 | 40 | 12 | ≈ 0.95 | 最快到达且基本不过冲 |
| C 过阻尼 | 40 | 40 | ≈ 3.16 | 单调爬升,但慢 |
(近似按 mj_fullM 读更准。)
观察点:
决定拉回去的劲,单摆静止时要至少能抵消重力对关节的力矩 ,否则永远差一截。 决定晃不晃,不要只靠 MJCF 里的damping帮忙——那是物理阻尼,不是控制器的 。 加大到某个点以后,仿真会开始出现明显的数值噪声甚至发散,这是采样周期 =timestep太大的信号,而不是 PD 理论错了。
1.5.4 实验 2:加上扰动看稳态误差
把 q_des = 0.8 保持不变,然后在第 3 秒给关节一个持续的外力矩:
if data.time > 3.0:
data.qfrc_applied[0] = 2.0 # 恒定外扰,单位 N·m
你会看到两种明显不同的行为:
- 纯 P 控制(
):关节稳在一个偏离 的新位置上,差的那一截正好是 。这就是"稳态误差"。 - PD 控制:在 P 的基础上抖动更小地到达那个偏移位置,但稳态误差依然存在——因为误差一恒定,微分项就为 0,不产生额外的修正力。
想把这个偏差也消掉,就要引入积分项
1.5.5 常见坑
把下面这些现象记一下,以后写 Pupper 步态、机械臂 IK 时还会反复遇到:
- 采样周期 ≠ 物理步长。MJCF 的
timestep是物理步长;如果你在真机上 500 Hz 读编码器,却在仿真里用 4 kHz 的 PD,那把仿真结果直接搬到真机会偏快。建议一开始就让 Python 循环和真机控制频率对齐(比如每 4 个mj_step更新一次ctrl)。 - 力矩饱和。
ctrlrange不是摆设——真电机有最大输出。用np.clip显式限幅,才能在仿真里重现真机的"我已经尽力了"。饱和之后,理论上的 也都会失真,别把公式当真理。 - 用
position执行器时kp/kv是绑在一起的,MuJoCo 默认 。很多人只填kp,结果得到一条剧烈震荡曲线,误以为 MuJoCo 有 bug——其实是 的后果。 - 别忘了补偿重力/摩擦。单关节目标是 0.8 rad,单纯
要战胜 ;如果电机力矩不够大,关节会卡在半空。严肃的机械臂控制会在 PD 外面再套一层mj_rnePostConstraint算的逆动力学补偿,本章不展开。 - 反作用力矩。关节力矩会通过父链传递到基座,在浮动基座(四足、人形)里尤其明显。第 5 章讲 trot 步态时会专门处理。
- 积分饱和 / Wind-up。这是 PID 的坑,不是 PD 的——但既然下一章就会用到积分项,提前记一笔:积分项要带限幅或"误差方向反转清零",否则长时间饱和后系统会严重过冲。
组队学习任务
- 写一个
pd_single_joint.py脚本,Kp / Kd 可配 - 画 3 组响应曲线(欠阻尼 / 临界阻尼 / 过阻尼),提交到组队群
- 用一句话回答:为什么只加 P 不加 D 时系统会抖?