3. PWM与电机调速:直流电机调速原理、占空比与平均电压的关系、死区时间与互补PWM
这一章,我们来聊聊PWM怎么让电机转起来、转多快。说白了,就是怎么用一串方波去控制一个直流电机。我刚开始接触这个的时候,也觉得挺神奇的——一个只有0和1的信号,居然能让电机平滑地调速。
3.1 直流电机调速原理
直流电机的转速,本质上由电枢两端的平均电压决定。电压高,转得快;电压低,转得慢。那问题来了:我们怎么改变这个电压?
传统做法是用一个可变电阻串联在回路里,分掉一部分电压。但电阻会发热,效率低得可怜。另一种是用线性稳压器,同样的问题——能量都耗在调整管上了。
PWM就不一样了。它不浪费能量,而是通过快速开关电源,让电机“看到”一个等效的平均电压。电机本身是个大电感,电流不会突变,所以它会把脉冲串“平滑”成连续的电流。
核心公式:
Vavg = Vin × Duty
其中 Duty 是占空比,范围 0~1(或 0%~100%)。
比如 12V 电源,50% 占空比,平均电压就是 6V。
我在一个项目里遇到过这种情况:电机低速时抖得厉害,怎么调PID都没用。后来发现是PWM频率太低,电机在每一个脉冲周期里都“喘口气”,转速自然不稳。把频率从100Hz提到1kHz,问题就解决了。嗯,这里要注意——频率不是越高越好,后面会讲。
3.2 占空比与平均电压的关系
占空比,就是高电平时间占整个周期的比例。你想想看,一个周期里,高电平时间越长,平均电压就越高。
| 占空比 | 高电平时间 (10kHz, 100μs周期) | 平均电压 (12V电源) | 电机状态 |
|---|---|---|---|
| 0% | 0 μs | 0 V | 停止 |
| 25% | 25 μs | 3 V | 低速 |
| 50% | 50 μs | 6 V | 中速 |
| 75% | 75 μs | 9 V | 中高速 |
| 100% | 100 μs | 12 V | 全速 |
这个关系是线性的,所以控制起来非常直观。你给一个0~100%的占空比,电机就给你一个0~100%的转速(理想情况下)。
但实际中,有一个“死区”——占空比太低时,电机可能根本不转。因为电机需要一定的启动转矩,克服静摩擦和惯性。我记得有一次调试,占空比从0%慢慢往上加,到8%左右电机才“咔”一下动起来。这个阈值,跟电机本身、负载、电源电压都有关系。
我的习惯:在软件里做一个“最小占空比”限制,比如 5%~10%,避免电机在死区里嗡嗡响却不转。同时做一个“启动加速”逻辑,先给一个较高的占空比让电机转起来,再降到目标值。
3.3 死区时间与互补PWM
这部分,是很多初学者容易踩坑的地方。尤其是做H桥驱动的时候。
先说说互补PWM。在H桥里,上管和下管不能同时导通,否则就是直通短路,电流会瞬间烧毁MOS管。所以我们需要一对互补的信号:上管导通时,下管必须关断;下管导通时,上管必须关断。
但问题来了——任何开关器件都有导通和关断的延迟。MOS管关断需要时间,导通也需要时间。如果上管还没完全关断,下管就开始导通了,那瞬间就短路了。
这就是死区时间(Dead Time)存在的意义。在互补PWM里,我们在两个信号之间插入一段“都关断”的时间,确保一个管子完全关断后,另一个才导通。
死区时间怎么设?我一般这样估算:
- 查MOS管数据手册,找到关断延迟时间 td(off) 和下降时间 tf
- 死区时间 > td(off) + tf,再留 20%~50% 的余量
- 比如一个MOS管关断延迟 50ns,下降时间 30ns,那死区时间至少设 100ns
我曾经踩过的坑:在一个无刷电机驱动项目里,死区时间设得太小(只有 50ns),结果上电瞬间MOS管直接冒烟。后来用示波器一看,上下管有 20ns 的重叠导通时间——就是这 20ns,把管子烧了。从那以后,我每次都会用示波器实测死区时间,而不是只看代码里设的值。
死区时间也不是越大越好。太大会导致:
- 波形失真,电机电流谐波增加
- 有效占空比降低,电机出力不足
- 体二极管导通时间变长,发热增加
所以,死区时间是一个权衡。我个人习惯在 100ns~500ns 之间,具体看MOS管的开关速度和PWM频率。频率越高,死区时间占比越大,对波形的影响也越明显。
3.4 实现方式:硬件 vs 软件
互补PWM和死区时间,可以用硬件实现,也可以用软件实现。
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 硬件(MCU内部定时器) | 精确、稳定、不占CPU | 配置复杂,受定时器资源限制 | 大多数电机控制项目 |
| 软件(GPIO翻转+延时) | 灵活,不受硬件限制 | 占用CPU,精度受中断影响 | 简单实验、低速应用 |
| 专用驱动芯片 | 内置死区、保护功能完善 | 成本高,灵活性差 | 大功率、高可靠性场合 |
我强烈建议用硬件方式。现在的MCU,比如STM32、TI的C2000系列,定时器都支持互补PWM输出和可编程死区插入。配置好了之后,CPU几乎不用管,硬件自动生成精确的波形。
下面是一个STM32的配置示例,用CubeMX生成代码,核心配置就这几行:
// 以TIM1为例,配置CH1和CH1N为互补输出
// 死区时间设为 144ns(时钟频率 168MHz,死区寄存器值 24)
TIM_OC_InitTypeDef sConfigOC = {0};
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 500; // 占空比 50%
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);
// 设置死区时间
htim1.Instance->BDTR = TIM_BREAK_OFF | 24; // 24个时钟周期 ≈ 144ns
你看,代码量很少。但要注意,BDTR寄存器里的死区时间值,不同MCU的计算方式不一样。有的直接写纳秒,有的要换算成时钟周期。一定要看数据手册。
一个小技巧:调试时先用示波器看PWM波形,确认死区时间对不对。我习惯把死区时间设得稍微大一点(比如200ns),等系统稳定了再慢慢调小,找到最佳值。