调舵机PD参数这件事,我见过太多人把它当玄学:先给一个比例增益,舵机抖了就减,不抖但跟手慢又加,全程靠手感在毛坯房里装修。等到真正把根轨迹法和稳定性分析吃透之后回头看,所谓手感,其实就是大脑在模糊估算阻尼比、相位裕度和闭环极点位置。这篇文章想把这条链路一次讲透:从舵机简化的传递函数怎么来,到根轨迹法为何能一眼看穿纯P与PD的本质差别,再到最终写成能在单片机上稳定跑的PD控制代码。适合三类人:正在学自控原理却怀疑"这玩意到底有啥用"的学生,做智能车、机械臂、仿生机器人时被舵机抖动折磨的嵌入式开发者,以及想弄明白PID参数到底是算出来的还是试出来的工程师。
1. 先把舵机拉进控制理论的世界:等效模型建立与关键参数估算
1.1 从直流电机到减速齿轮箱,舵机内部到底有什么
绝大多数舵机拆开之后,核心部件是一台直流有刷电机、一套减速齿轮组、一个位置反馈电位计(好一点的用磁编码器),再加一块小型驱动电路。航模舵机内部通常自带一个位置闭环,MCU给得越多,舵机内部把电位计角度追上给定值的能力越强。这个"内部闭环"意味着外部看到的行为更像一个带惯性的一阶或二阶伺服系统,而不是纯粹的直流电机。
不过我们自己设计机械臂或智能车转向机构时,往往把舵机当成一个可以被外部MCU直接控制角度的执行器。此时外部任务其实是"外环位置控制"——我需要舵机角度精确到1度以内、响应快且不超调,这就需要理解舵机这个对象的外部动力学特性。把它当成黑盒测响应曲线是工程做法,但为了弄懂根轨迹画的是什么,得先把它简化成白盒模型。
1.2 电气方程式一摆,二阶模型就出来了
直流有刷电机的电枢等效电路满足电压方程:u(t) = R·i(t) + L·di/dt + e(t),其中e(t)是反电动势,与转速成正比。电机产生的电磁转矩与电流成正比:T(t) = Kt·i(t)。转子上的力矩平衡方程是:J·dΩ/dt + B·Ω = T(t),这里J是转动惯量(包含转子、减速齿轮和负载折算值),B是粘性阻尼系数。
把电压方程和力学方程联立,忽略掉电感L的影响(电气时间常数远小于机械时间常数,这是小功率舵机成立的典型假设),从输入电压u到输出角速度Ω的传递函数就会简化成一个一阶惯性环节:
G_v(s) = Ω(s) / U(s) = Km / (Tm·s + 1)
其中Tm就是机电时间常数,物理意义可以理解为"电机从静止加速到63.2%最终转速所需的时间"。角速度再积一次分变成角度,所以从电压到舵机输出轴角度的开环传递函数就是:
G(s) = θ(s) / U(s) = Km / [s·(Tm·s + 1)]
这个式子很关键:一个积分环节(体现角度是速度的积分)串联一个一阶惯性环节(体现电机和机械系统的惯性),这就是后面所有根轨迹分析的起点。只要不追求极高频特性,这个二阶模型已经能解释绝大多数舵机抖动和超调问题。
1.3 时间常数到底是多少量级,实测怎么测
典型小型舵机的Tm大致在20-100毫秒量级,负载越大,折算到电机轴上的转动惯量越大,Tm越大,响应越慢。控制理论里经常假设"对象已知",工程里最缺的就是这个已知值。测Tm不用太高深的设备:给舵机开环一个阶跃占空比,比如从中间位置直接跳到某个方向,用示波器或逻辑分析仪看电位计反馈电压的变化轨迹。
把阶跃响应曲线记录下来,找到它从起点上升到最终稳定值的63.2%所花的时间,这个时间就是Tm(严格说还包含内部闭环的影响,但作为工程估值足够用了)。我自己实测过好几款常见舵机,空载Tm普遍在30-60ms之间,带负载后可以涨到100ms以上。这个数字直接决定后面Kp、Kd的数量级,千万不要用别人文章的固定参数抄作业。
2. 根轨迹法到底在画什么:增益一涨,闭环极点就往右跑
2.1 闭环极点和稳定性之间的对应关系
判断一个线性系统稳不稳定,本质就是看闭环极点全部落在复平面左半平面。极点的实部为负,说明暂态分量随时间衰减;实部为正,说明时间一长信号指数发散。控制器的增益K从0往无穷大调整时,闭环极点会连续移动,根轨迹法做的就是画出这条"极点移动路线图"。
幅值条件和幅角条件是根轨迹的理论内核:闭环特征方程1 + C(s)G(s) = 0要成立,等价于开环传递函数的幅值为1、相角为180°加2kπ。通俗讲,系统在某个频率上把信号放大到自身能维持振荡的程度,并且相位刚好反相,就会自激。这个反相条件正是"相位裕度归零即振荡"的由来。不过实际画图不需要每点都套公式,记住几条绘制规则就够推导舵机的情况了。
2.2 绘制根轨迹的三条规则,足以看懂舵机的全部行为
规则一是根轨迹的起点是开环极点,终点是开环零点或无穷远。增益K=0时闭环极点就是开环极点,K=∞时极点会被吸引到零点或无限远。规则二是实轴区段判断:实轴上某一段右侧的开环极点数与零点数之和为奇数,这段就在根轨迹上。规则三是渐近线:有n个极点、m个零点时,有n-m条分支趋向无穷远,渐近线与实轴的交点是极点之和减去零点之和再除以n-m,角度由(2k+1)π/(n-m)决定。
对纯P控制下的舵机,开环有两个极点:一个在原点0,对应"角度是速度积分"的积分环节;一个在负实轴上的-1/Tm,对应机械惯性。有了零点时它们的终点会更复杂,但起点和大致走向这几条足够用了。分离点、会合点的计算更精细一些,读者需要时可以翻教材,关键结论在那里诞生。
2.3 纯P情况下,舵机根轨迹的三种命运
把控制器取为C(s)=Kp,开环增益写成K=Kp·Km,则闭环特征方程为Tm·s² + s + K = 0。用求根公式能得到:
s1,2 = [-1 ± sqrt(1 - 4·Tm·K)] / (2·Tm)
K从小到大变化时,这根轨迹先在实轴上相向而行:一个极点从原点往负方向走,另一个从-1/Tm往正方向走,在s=-1/(2·Tm)处相遇。这个点是分离点,对应临界阻尼状态。K继续增大,两个极点变成共轭复数,一条垂直向上、一条垂直向下,虽然理论上仍然在左半平面,但阻尼比越来越小,系统越来越容易抖。
这里有个和直觉冲突的点:理想二阶模型下,纯P控制在任何正增益下理论都是稳定的,因为它永远不会跨过虚轴。可实际舵机为什么Kp稍大就肉眼可见地抖?因为实际模型里还有延迟和齿轮间隙。延迟让根轨迹出现"额外拐弯",增益增大到某个临界值时,一对共轭极点就会横穿虚轴进右半平面。所以纯P控制不是"不能稳",而是"稳定边界紧贴实际可用的响应速度",留不下余量。
3. 稳定性分析不是纸上谈兵:劳斯判据、临界增益与相位裕度
3.1 用劳斯表快速揪出临界增益
劳斯判据能直接告诉一个系统在什么参数组合下保持稳定,不需要求特征根。把特征方程写成标准形式后,列出劳斯表,第一列全为正系统就稳定;第一列出现变号,变号次数就是右半平面极点数。对舵机这类二阶模型,特征方程是Tm·s² + s + K = 0,劳斯表第一列是[Tm, 1, K],只要K>0第一列全正,说明简单二阶模型无条件稳定。
但如果把舵机内部闭环延迟、PWM刷新周期等效成一个滞后环节,模型强度立刻不一样。假设等效延迟为τ,用一阶帕德近似表示延迟,开环传递函数变成:
G(s) ≈ K·(1 - τ·s/2) / [s·(Tm·s + 1)·(1 + τ·s/2)]
特征方程展开成三阶:a₀s³ + a₁s² + a₂s + a₃ = 0。列劳斯表后,稳定条件会约束出K的上限。以Tm=0.1s、τ=0.01s为例,计算得到临界增益约为K_crit≈102。也就是说,如果P增益对应的等效开环增益超过这个值,哪怕PID没写错,系统也会因为延迟而失稳。这个数字比很多人凭感觉认为的"大一点应该没事"低得多。
3.2 相位裕度为什么锚定在45到60度
相位裕度是开环频率特性在穿越频率(幅值等于1处的频率)那一点,相位距离-180度还差多少。裕度30度,系统勉强稳定但阶跃响应会有明显振荡;裕度45度,表现中等;60度以上,响应稳健且超调受控。控制工程常用的"45到60度"不是拍脑袋规定,它对应阻尼比大约0.5到0.7,超调量大致在5%到16%之间。舵机外环如果超调20%已经不可接受,因为机械限位可能撞齿或扫齿,所以外环设计我习惯把相位裕度目标定在55到60度。
纯P控制下的穿越频率通常是令|K/(jω·(1+jωTm))|=1解出来的,假设Tm=0.1s,K=40,可估算穿越频率约20rad/s。此时相位约为-90°-arctan(20×0.1)= -90°-63.4°= -153.4°,还没有延迟就只剩26.6度裕度,要是再算上PWM刷新和采样延迟,大约只剩15到20度,表现就是"勉强能用但容易抖"。这解释了为什么很多人说"光调P调不出跟手又稳定的手感"。
3.3 延迟这个隐形杀手如何在模型里显形
PWM舵机的控制周期通常是50Hz(20ms刷新一次)。如果位置环的输出和PWM刷新不在同一时刻更新,最坏情况下会引入接近20ms的等效相位延迟。频率20rad/s处,20ms延迟对应的相位滞后是0.4rad≈23度,足以把整个系统的相位裕度吃掉一大半。这就是为什么工程里把"位置环速率"和"PWM刷新率"分得越清,实际表现越好的原因之一。
延迟在根轨迹图上表现为增加了一个额外的"靠近原点的极点",会显著改变根轨迹的走向,让分离点右移、临界增益下降。用simulation跑一遍就能直观看到:熬得住纯P高增益的理想模型,叠加20ms延迟后就出现极限环。这些结论不是为了考试,而是为了解释现实里舵机抖动的根源——多数时候不是代码写错,是增益逼近了延迟决定的稳定边界。
4. PD控制的本质:把一个零点"粘"在根轨迹上改变走向
4.1 传递函数多了一项,根轨迹多了一个零点
PD控制器的传递函数是C(s) = Kp + Kd·s,把它写成Kd·(s + Kp/Kd)就一目了然:D项附加了一个可移动的零点,位置在s = -Kp/Kd。这个零点不是可有可无的装饰,它改变了根轨迹的终点。原纯P系统两条根轨迹分支最终走向无穷远。加入零点后,其中一条分支从原点出发走向这个零点就结束了,另一条分支从-1/Tm出发继续沿实轴驶向负无穷。物理直觉是:系统极点被一个"左半平面的引力源"牵引,阻尼自然提升。
零点越靠近原点(即Kp/Kd越小),对极点轨迹的拉力越早显现,阻尼效果越强。但零点也不能设得太小,否则主导极点位置偏低,系统响应频带窄,舵机会显得"肉"。零点设得太远(Kp/Kd很大),PD几乎退化成纯P,D项起不到作用。实际设计中,我通常让零点与原点的距离取在1/Tm附近量级,再根据实测阶跃响应微调两个增益比例。
4.2 微分项的真正意义不是"加速",而是"阻尼"
很多教程把微分环节解释成"预测未来误差变化",这种说法容易引导人以为D是加速手段。从特征方程看更清楚:加入PD后闭环特征方程为:
Tm·s² + (1 + Kd·Km)·s + Kp·Km = 0
对比标准二阶系统s² + 2ζωn·s + ωn² = 0,会发现Kd直接贡献了s项系数,也就是阻尼比ζ。Kp贡献了常数项,也就是自然频率ωn(决定响应快慢)。所以PD的分工非常明确:P决定系统的"刚度"和频带,D决定"阻尼"。想要反应快又不抖,公式上就是尽量提高ωn同时把ζ维持在0.6到0.8之间。
舵机换向瞬间,误差突然变大,如果没有D,比例项的输出会猛推一下,齿轮和电位计都跟着剧烈振荡。有了D项,它检测到角度快速变化并反向抑制,让推进变得柔和。从根轨迹视角看,Kd增大等价于零点向左半平面深处移动,把闭环极点从靠近虚轴的"易振区"拉向负实轴附近。这是"加了D稳了"的本质。
4.3 阻尼比、超调量与调节时间的换算关系
设计参数前先建立心理标尺:二阶系统超调量Mp ≈ e^(-πζ/sqrt(1-ζ²))。ζ=0.4时超调约25%,基本"抖得明显";ζ=0.7时超调约4.6%,体感顺滑;ζ=1时无超调但会有响应偏慢的临界阻尼手感。调节时间ts ≈ 4/(ζωn)(按5%误差带估算)是做PD设计最常用的式子。
假设期望舵机1秒内到达目标且超调小于5%,可以设定ζ=0.7,则ωn至少为4/(0.7×1)=5.7rad/s,工程上留倍量取10到15rad/s。如果期望0.3秒到位,ωn就得20rad/s以上。这个计算把"手感"变成了具体的数字,也直接决定下一章的Kp、Kd初值怎么取。纯P只能在这组期望参数下把Kp调得妥协,PD则能同时满足快和稳两个约束。
5. 参数整定从拍脑袋变成算出来:一套可复现的操作流程
5.1 第一个动作:开环阶跃测试,把Tm捞出来
别急着改代码,先把舵机处于开环受控状态。所谓开环,就是直接给舵机一个固定PWM占空比,让它从中间位置朝一个方向转动。注意要避免长时间满占空比堵转,测试时间控制在几百毫秒内。用ADC采样电位计反馈电压(或编码器角度),记录角度阶跃响应曲线,找63.2%的时间点。
我自己实测过某型号舵机,空载Tm约45ms,加上底盘转向连杆机构后负载惯量变大,Tm涨到约80ms。所以负载变化后必须重测。测出来的Tm直接代入下一节公式,比查表可靠得多。
5.2 期望阻尼比与期望自然频率如何定
先回答两个问题:允许的最大超调是多少?期望的调节时间是多少?对于舵机位置控制,我一般把超调上限压在5%以内,取ζ=0.7作为设计点。调节时间看应用:转向联动允许稍慢取ts约0.5s,机械臂关节希望0.2到0.3s到位就取ωn=20左右。
如果你偏好工程裕量,可以先按ζ=0.8、ωn=15rad/s设计,跑通后再根据实测逐步提高ωn。这样系统虽然略保守,但不会一开始就抖到让你怀疑数学。这套"先保守后激进"的策略在嵌入式调试里非常实用。
5.3 由特征方程反解Kp和Kd初值
回到特征方程Tm·s² + (1 + Kd·Km)·s + Kp·Km = 0,把Kp·Km和Kd·Km各看成一个整体变量更方便:
Kpu = Kp·Km = ωn²·Tm
Kdu = Kd·Km = 2ζ·ωn·Tm - 1
代入Tm=0.1s、ωn=20rad/s、ζ=0.7:Kpu = 400×0.1 = 40,Kdu = 2×0.7×20×0.1 - 1 = 1.8。这里的Kpu就是代码里Kp的初始标定值(单位是PWM输出变化量/误差角度单位的复合量纲),Kdu同理。
实际代码中的Kp还要经过"角度误差单位到PWM脉宽单位"的换算,直接套这个初值后观察阶跃响应,再调增益即可。我是这么处理的:先用计算值跑,如果超调明显,保持Kp不动、Kd提高到计算值的1.5倍;如果响应太肉,把Kp提高20%,再观察阻尼是否被拉低。每一步只改一个参数,记录现象,比同时乱拧高效得多。
5.4 算出来的参数到手后,还要做哪些实测修正
理论给了起点,实测负责落点。阶跃测试时给一个小幅角度指令,比如30度,观察响应是否有超调、有无抖震。超调大就加D,响应慢就加P、但如果加P后又开始抖,说明D需要同步跟上。持续高频抖动多半是微分噪声放大,不是阻尼不够,处理方式在下一章讲滤波器。
修正过程中要记住根轨迹的走向:增大Kp会让闭环极点沿轨迹前移,增大Kd会让零点左移、极点往里拉。每加一次参数,脑内过一遍这个画面,调试就不会显得混乱。我习惯把每次参数和响应现象记成表,几十组数据下来,你对"手感"的理解会越来越接近控制理论的抽象结论。
6. 一份可直接落地的舵机PD控制代码实现
6.1 硬件连接与时基约定:50Hz PWM背后的事
舵机信号线输入的PWM脉宽通常在1000到2000微秒之间,中值1500微秒,刷新周期20ms(50Hz)。你在MCU上要做的,就是通过定时器输出一个周期20ms、脉宽可调的PWM信号。设置定时器时,预分频让计数时钟为1MHz,自动重装载值设成20000,这样计数器每计到20000就是20ms。通道比较值写入1000到2000,直接对应脉宽微秒数。
位置反馈我用电位计接ADC采集,测量范围根据舵机机械行程映射到角度。电机的方向映射和电位计安装方向有关,代码里如果出现"越控越偏"的情况,优先检查正负号,这是调舵机时最常见的低级但致命的问题。PWM极性设置成高电平有效,舵机在脉宽1000微秒时处于一侧极限位。
6.2 位置式PD核心代码:结构体定义与更新函数
以下代码使用通用C语言描述,不依赖具体MCU库,重点在控制逻辑本身。核心思路是:每个控制周期读取当前角度,计算误差,用误差乘Kp,用角度变化率乘Kd并反相叠加,输出限幅后写进PWM比较寄存器。
typedef struct { float kp; // 比例增益初值,来自特征方程反解 float kd; // 微分增益初值 float dt; // 位置环周期,秒 float target_deg; // 目标角度 float prev_feedback; // 上一拍的角度反馈 float filtered_deriv; // 滤波后的微分项 float deriv_alpha; // 微分低通滤波系数 float deadzone_deg; // 死区阈值 uint16_t pwm_min_us; // 最小脉宽 uint16_t pwm_mid_us; // 中位脉宽 uint16_t pwm_max_us; // 最大脉宽 } ServoPD; float map_angle_to_pwm_us(ServoPD *s, float angle_deg) { // 假设 -90~+90 度映射到 1000~2000us,实际按舵机行程调整 return s->pwm_mid_us + (angle_deg / 90.0f) * 500.0f; } void servo_pd_update(ServoPD *s, float angle_feedback) { float error = s->target_deg - angle_feedback; // 死区处理:误差小到一定程度不做动作,避免极限环 if (fabsf(error) < s->deadzone_deg) { s->prev_feedback = angle_feedback; return; } // 关键:对反馈角度做微分,而不是对误差做微分 float raw_deriv = (angle_feedback - s->prev_feedback) / s->dt; s->prev_feedback = angle_feedback; // 一阶低通滤波,压制电位计ADC噪声 s->filtered_deriv = s->deriv_alpha * s->filtered_deriv + (1.0f - s->deriv_alpha) * raw_deriv; // PD合成:比例项用误差,微分项用反馈变化率取负 float u = s->kp * error - s->kd * s->filtered_deriv; uint16_t pwm_us = (uint16_t)(s->pwm_mid_us + u); if (pwm_us > s->pwm_max_us) pwm_us = s->pwm_max_us; if (pwm_us < s->pwm_min_us) pwm_us = s->pwm_min_us; set_servo_pwm_us(pwm_us); }6.3 三个必须加的保护:限幅、死区、微分滤波
限幅是最容易理解的一层:PD输出不能超过PWM脉宽允许的范围,否则舵机会堵转并持续发热。死区则解决"到位后微小误差导致持续修正"的问题,典型死区设为0.3到0.5度就够,太大影响定位精度。微分滤波是很多人忽略的一层:ADC反馈噪声经差分后会被放大数倍,导致输出高频颤动。我用的是一阶低通,系数deriv_alpha越接近1滤波越强,但相位滞后也越大。
位置环周期设为2ms(500Hz)时,微分低通截止频率选10到20Hz合适。截止频率过低会让D项反应太慢,等于没加阻尼;过高则噪声滤不干净。公式近似为alpha ≈ (2π·fc·dt) / (1 + 2π·fc·dt),fc=15Hz、dt=0.002s时alpha约为0.16,实测稳定可用。
6.4 增量式PD与位置式PD怎么选
如果整个系统的执行器是舵机这种"输出即绝对位置"的器件,位置式PD天然合适。增量式PD输出的是本次相对上次的增量,适合对象本身有积分性质或需要平滑起步的场合,比如电机速度环。增量式不需要单独的滤波项也能让输出变化平缓,但存在累积误差问题,且目标突变时容易产生较大增量冲击。
对舵机外环,我建议直接使用位置式。原因很简单:位置输出限幅直观、调试时容易从示波器读脉宽,而且代码逻辑和上一章的理论推导一一对应。如果你在多关节机械臂里用舵机做关节,后续要在不同负载工况切换参数,位置式结构也更容易做增益调度。
7. 实测中那些理论不会讲的坑与对策
7.1 采样周期、PWM刷新率和微分截止频率的配合
位置环跑500Hz、PWM还是50Hz是常见搭配,但要注意输出更新时刻和PWM脉冲起始时刻的对齐。如果位置环输出发生在PWM脉冲中途,下一次PWM才能生效,相当于在控制回路里又引入几毫秒延迟。折中办法是把位置环计算放在定时器PWM周期起始中断里执行,让输出和PWM周期同步对齐。
微分截止频率要随着位置环采样率调整。采样率越高,可用微分带宽越高,但ADC噪声也随之增加。实测下来,500Hz的位置环配合10到20Hz的微分滤波是个平衡点;如果位置环降到100Hz,微分截止频率还要再降,否则滤波相位滞后会吃掉D项本身带来的阻尼。
7.2 电位计噪声和回程差会联手骗过微分项
电位计在零位附近ADC读数抖动3到5个码非常正常,直接换算成角度在0.3度量级,而微分项恰恰对这类高频小幅抖动敏感。另一种麻烦是减速齿轮回程差:换向时齿轮先吃掉间隙,角度反馈有一小段停顿,微分项会把它误判成"速度为零",导致输出变化中断,体现在响应曲线上就是换向死区。
对策分两步:ADC侧先做4到8次滑动平均,再进PD计算;死区不要只靠控制端消除,还可以在PD输出侧做微小的回程补偿——当误差方向改变时,把输出额外叠加一个固定脉宽增量,帮助舵机快速跨过齿轮间隙。补偿值需要通过实验测出,通常几十微秒脉宽量级。
7.3 电源电压跌落会让你的PD参数瞬间失效
舵机堵转电流很大,如果电源线压降严重,实际加在电机上的电压远低于额定值,电机力矩不足,根轨迹里那个"积分环节"表现就会变得迟滞。此时加再大的Kp也换不来快速响应,只会让误差持续堆积。解决方向是电源侧就近并联大容量电解电容,同时缩短舵机电源线和MCU供电线的公共路径,把模拟反馈地线与功率地线分开走。
调试中如果发现"参数突然不可控"或者"同一个参数换块电池表现完全不同",优先怀疑电源跌落。把舵机电源和逻辑电源分开供电是最省心的做法,调试时用稳压电源限流也能排除不少变量。纯靠程序补偿供电不足,永远只是治标不治本。
7.4 最后分享一个我常用的调试顺序
先把滤波和死区全部关掉,Kp取计算初值的30%,Kd取0,跑一次阶跃确认系统存活。然后逐步加Kp,观察何时出现轻微振荡,在这个临界值上把Kp回调到60%。接着加Kd,每次增加20%左右,直到振荡消失并留有余量。之后再打开滤波和死区,你会发现在低增益环境下微分噪声影响很小,滤波参数可以给得比较宽裕。
最后再做一次完整阶跃测试,如果超调仍在5%以内、调节时间达标,这套参数就可以收工了。如果超调偏大,不要只动Kd,先看一眼微分滤波是否滞后太多,再考虑让D项更快响应。这个顺序帮我省下过大量盲目试参的时间,也彻底改变了我对"调参玄学"的看法——根轨迹法和稳定性分析不是黑板上的理论,它们就是调试时最可靠的导航仪。