舵机控制协议:PWM信号基础、频率与占空比、标准舵机控制信号
说到舵机控制,我第一个想到的就是PWM。说白了,PWM就是通过快速开关信号,让舵机“以为”自己收到了一个稳定的电压。你想想看,如果我们直接给舵机一个模拟电压,电路会变得又复杂又贵。PWM用数字引脚就搞定了,这就是它流行的原因。
PWM信号基础
PWM的全称是脉冲宽度调制。它的核心思想很简单:用一串方波信号,通过改变高电平的宽度来传递信息。我在做第一个阿克曼底盘项目时,一开始没搞懂PWM和普通方波的区别,结果舵机抖得像筛糠一样。
一个完整的PWM信号包含三个要素:
- 周期:一个完整脉冲的时间长度
- 频率:每秒有多少个周期,单位是Hz
- 占空比:高电平时间占整个周期的百分比
举个例子,如果周期是20ms,高电平是1.5ms,那占空比就是7.5%。这个比例直接决定了舵机的转角。
核心概念:PWM不是模拟信号,但可以模拟出类似模拟信号的效果。舵机内部有个比较器,它会对比输入PWM和内部电位器的反馈信号,从而确定转动角度。
PWM频率与占空比
频率和占空比,这两个参数经常被人搞混。我刚开始学的时候也犯过这个错——以为频率越高越好。其实不是这样的。
频率的选择:
- 标准舵机:50Hz(周期20ms),这是行业标准
- 数字舵机:可以支持更高频率,比如200Hz甚至更高
- 普通直流电机:通常用1kHz-10kHz
占空比的作用:
- 对于标准舵机,占空比5%-10%对应0°-180°
- 占空比7.5%对应中位(90°)
- 占空比超出范围可能导致舵机堵转或损坏
我的经验:我曾经在一个项目中用了100Hz的PWM去控制标准舵机,结果舵机发热严重,而且定位不准。后来查资料才发现,标准舵机内部的RC滤波电路就是按50Hz设计的。频率不对,滤波效果就差了。
标准舵机控制信号(50Hz,0.5ms-2.5ms)
这是舵机控制里最经典的标准。几乎所有 hobby 级别的舵机都遵循这个协议。我习惯把它叫做“20ms规则”。
具体来说:
- 周期固定为20ms(50Hz)
- 高电平脉冲宽度在0.5ms到2.5ms之间
- 0.5ms对应0°(或-90°,取决于舵机型号)
- 1.5ms对应90°(中位)
- 2.5ms对应180°(或+90°)
| 脉冲宽度 | 占空比 | 理论角度 | 常见应用 |
|---|---|---|---|
| 0.5ms | 2.5% | 0° | 左极限 |
| 1.0ms | 5.0% | 45° | 左转 |
| 1.5ms | 7.5% | 90° | 中位(直行) |
| 2.0ms | 10.0% | 135° | 右转 |
| 2.5ms | 12.5% | 180° | 右极限 |
注意:不同品牌的舵机,0°和180°对应的脉冲宽度可能有细微差别。比如有些舵机是0.6ms-2.4ms。我曾经因为没看舵机手册,直接用标准值去控制,结果舵机咔咔响,差点烧了。所以拿到新舵机,第一件事就是看数据手册。
PWM信号的生成方式
在嵌入式系统里,生成PWM信号主要有三种方式:
- 硬件PWM:用定时器的比较输出功能,精度高,不占CPU
- 软件PWM:用GPIO加延时,简单但精度差
- 专用PWM芯片:比如PCA9685,可以同时控制16路舵机
我个人强烈推荐用硬件PWM。为什么?因为软件PWM在中断多的时候会抖动。我在一个项目中用Arduino的Servo库控制4个舵机,结果CPU一忙,舵机就开始抽搐。后来换成硬件PWM,问题就解决了。
下面是一个用STM32生成标准舵机信号的代码片段:
// STM32 硬件PWM配置 - 50Hz, 1.5ms中位
TIM_HandleTypeDef htim2;
void MX_TIM2_Init(void) {
TIM_OC_InitTypeDef sConfigOC = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72 - 1; // 72MHz / 72 = 1MHz
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 20000 - 1; // 1MHz / 20000 = 50Hz
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim2);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 1500; // 1.5ms 中位
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
}
// 设置舵机角度
void set_servo_angle(uint16_t angle) {
// angle: 0-180
uint16_t pulse = 500 + (angle * 2000 / 180);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pulse);
}
避坑指南:我曾经在设置预分频器时算错了数,导致PWM频率变成了48Hz。舵机虽然能动,但总有一种“卡顿感”。后来用示波器一看,周期是20.8ms,不是标准的20ms。嗯,这种小误差在低速时感觉不到,但高速转向时就会出问题。
PWM信号的质量问题
信号质量直接影响舵机控制精度。我总结了几点:
- 抖动:脉冲宽度的微小变化会导致舵机抖动。硬件PWM的抖动通常在纳秒级,软件PWM可能达到微秒级
- 噪声:电源纹波会耦合到PWM信号上。我在底盘上加了100μF的电容后,抖动明显减少
- 电平匹配:3.3V的MCU控制5V的舵机,需要电平转换。直接连接可能导致PWM信号幅值不够
说到电平匹配,我记得有一次用ESP32(3.3V)直接驱动MG996R舵机(5V),结果舵机只能转到90°左右。用示波器一看,PWM高电平只有3.0V,舵机内部的比较器阈值是3.5V。加了个电平转换模块后,问题就解决了。
这张图展示了不同角度对应的脉冲宽度。你可以看到,脉冲宽度从0.5ms到2.5ms线性变化,而周期始终是20ms。这就是标准舵机控制协议的精髓——固定频率,变化占空比。
在实际项目中,我建议你用示波器先看一下生成的PWM波形。很多时候代码看起来没问题,但实际波形就是不对。比如定时器的预分频器设置错了,或者占空比计算有偏差。用示波器一看,什么都清楚了。
总结一下:PWM控制舵机,说白了就是三件事——频率要对(50Hz),脉宽要准(0.5ms-2.5ms),信号要稳(硬件PWM)。这三条做到了,舵机控制就成功了一大半。