3. 电机控制基础:H桥原理,PWM调速与死区时间设置
好,咱们今天聊点硬核的。H桥、PWM、死区时间,这三个词你肯定不陌生。但说实话,我见过不少工程师,干了三五年,对死区的理解还停留在「加个延时就行」的层面。嗯,这其实挺危险的。
咱们一步步来。先搞懂H桥到底在干嘛,再说PWM怎么调速,最后聊聊那个让人又爱又恨的死区时间。
3.1 H桥原理:说白了就是个换向开关
H桥这个名字,来源于它的电路形状像个大写的字母「H」。四个开关管,两个在上,两个在下,中间挂着电机。你想想看,这不就是个换向开关吗?
我习惯把四个开关管分别叫做:Q1(左上)、Q2(右上)、Q3(左下)、Q4(右下)。
电机要正转,怎么办?导通Q1和Q4。电流从电源正极,经过Q1,流过电机,再经过Q4,回到电源负极。就这么简单。
要反转呢?导通Q2和Q3。电流方向反过来了。
这里有个关键点——绝对不能同时导通同一侧的上下两个管子。比如Q1和Q3同时导通,那就是电源正极直接对地短路。我管这叫「直通」,后果嘛...冒烟是轻的,严重的话能把MOSFET炸飞。
核心要点:
- 正转:Q1 + Q4 导通
- 反转:Q2 + Q3 导通
- 刹车:Q1 + Q2 导通(或 Q3 + Q4)
- 滑行:全部关断
我在项目中遇到过一件事。有个同事,调试时发现电机一启动就抖得厉害。查了半天,发现是H桥的驱动时序写错了,正转和反转之间没有加延时,导致瞬间短路。嗯,那次教训挺深刻的。
3.2 PWM调速:不是调电压,是调占空比
很多人以为PWM调速就是调电压。其实不是。你想想看,电机两端接的是12V或者24V,电压是固定的。那怎么调速?
答案是:通过快速开关,改变平均电压。
PWM的全称是脉冲宽度调制。说白了,就是在一个固定的周期里,让管子导通一段时间,关断一段时间。导通时间占整个周期的比例,叫占空比。
占空比50%,电机得到一半的功率。占空比100%,全功率。占空比0%,停转。
我习惯用这样的公式来理解:
电机平均电压 = 电源电压 × 占空比
举个例子。电源12V,占空比75%,那电机感受到的平均电压就是9V。转速大概就是全速的75%。
这里有个坑——PWM频率不能太低,也不能太高。
- 频率太低(比如几十Hz):电机会发出嗡嗡声,甚至能听到开关的咔嗒声。人耳能听到20Hz到20kHz,你想想看,要是PWM频率落在人耳范围内,那噪音得多烦人。
- 频率太高(比如几百kHz):开关损耗会急剧增加。MOSFET每开关一次,都有能量损耗。频率越高,损耗越大,发热越严重。
我个人建议,车载转向电机的PWM频率选在10kHz到20kHz之间。这个范围人耳听不到,开关损耗也还能接受。
我的经验:
我曾经在一个项目里把PWM频率设到了50kHz,结果MOSFET烫得能煎鸡蛋。后来降到16kHz,温度直接降了30度。频率这东西,不是越高越好。
3.3 死区时间:一个不得不加的「安全间隙」
好,现在咱们聊聊死区时间。这是H桥控制里最容易出问题的地方。
你想想看,H桥在换向的时候,比如从正转切换到反转。正转时Q1和Q4导通,反转时需要Q2和Q3导通。那中间这个切换过程,怎么保证不短路?
假设我先关掉Q1和Q4,再打开Q2和Q3。听起来没问题对吧?但实际中,开关管不是瞬间完成的。关断需要时间,导通也需要时间。
如果Q1还没完全关断,Q3就已经导通了——直通!
所以,我们需要在切换时,插入一段「所有人都关断」的时间。这段时间就是死区时间。
我画个图帮你理解:
你看,Q1关断后,要等一段时间,Q3才能导通。这段时间里,两个管子都是关断的。这就是死区。
死区时间设多长?这取决于你用的MOSFET或者IGBT的开关速度。
| 开关器件类型 | 典型开关时间 | 建议死区时间 |
|---|---|---|
| 低速MOSFET | 100-200ns | 500ns - 1μs |
| 高速MOSFET | 20-50ns | 100-300ns |
| IGBT | 200-500ns | 1-3μs |
| 智能功率模块 | 内置死区 | 按手册配置 |
警告:
死区时间不是越大越好。死区太长,电机会出现明显的「台阶感」,低速时尤其明显。死区太短,又有直通风险。我曾经在一个项目里,因为死区设了2μs,电机在低速时抖得像筛子。后来调到500ns,问题解决了。
3.4 代码实现:一个简单的PWM+死区示例
说了这么多理论,咱们看看代码怎么写。我用的是STM32的HAL库,但思路是通用的。
// 配置定时器PWM输出
void Motor_PWM_Init(void)
{
TIM_HandleTypeDef htim1;
TIM_OC_InitTypeDef sConfigOC = {0};
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0; // 不分频
htim1.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1; // 中央对齐模式
htim1.Init.Period = 1000; // PWM频率 = 72MHz / 1000 = 72kHz
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_PWM_Init(&htim1);
// 配置通道1(上管PWM)
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0; // 初始占空比0%
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);
// 配置通道2(下管PWM)
sConfigOC.Pulse = 0;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2);
// 启动PWM输出
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
}
// 设置电机转速和方向
void Motor_SetSpeed(int16_t speed)
{
// speed范围:-1000 ~ 1000
// 正数:正转,负数:反转
if (speed > 0)
{
// 正转:上管PWM,下管常关
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, speed);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 0);
}
else if (speed < 0)
{
// 反转:下管PWM,上管常关
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, -speed);
}
else
{
// 停止
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 0);
}
}
注意看,我用了中央对齐模式。为什么?因为中央对齐模式下,PWM的上升沿和下降沿是错开的,天然就有一定的死区效果。当然,这不能完全替代硬件死区,但能减少死区设置的难度。
我的习惯:
我一般会在硬件上再加一级死区插入电路。比如用74HC14或者专门的MOSFET驱动芯片,它们内部有死区时间设置引脚。这样即使软件出了bug,硬件也能兜底。
3.5 避坑指南
最后,我总结几个我踩过的坑,你遇到了可以少走弯路。
- 死区时间不是对称的:上管关断和下管导通的时间可能不一样。最好用示波器实测,别光看手册。
- PWM频率和死区时间要匹配:频率越高,死区占的比例越大,有效占空比范围就越小。比如72kHz的PWM,死区1μs,那占空比损失就很大。
- 启动时要小心:电机启动瞬间电流很大,可能达到额定电流的5-10倍。这时候如果死区设置不当,很容易炸管。
- 别忘了续流二极管:H桥里的MOSFET本身有体二极管,但有时候体二极管不够用。我建议在电机两端并联一个快恢复二极管,能有效抑制反电动势。
我曾经在一个量产项目里,因为死区时间设得太紧,导致一批产品在高温下频繁炸管。后来查出来,是温度升高后MOSFET的开关速度变慢了,死区不够用。从那以后,我设计死区时间都会留30%的余量。
好了,H桥、PWM、死区时间,这三个东西你搞明白了,电机控制的基础就算打牢了。下一节咱们聊聊更高级的东西——电流环和速度环的控制。嗯,到时候你会发现,今天讲的这些东西,全都会用上。
公众号:蓝海资料掘金营,微信deep3321