ESP32 无感 BLDC 实战:esp_sensorless_bldc_control 比较器过零检测与六步换相全流程
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
如果你正在用 ESP32 驱动无刷电机、又不想装霍尔传感器,esp-iot-solution仓库里的esp_sensorless_bldc_control组件有一条绕不开的路:用三路硬件比较器输出反电势过零电平,配合六步换相表完成无感方波驱动。本文带你从 30 行的最小代码起步,一路走到调参排障。
⚡ 最小代码跑通比较器模式
先把"长什么样"摆出来。下面是从测试用例 test_comparer.c 里抽出的骨架:上桥臂用 LEDC 调 PWM,下桥臂用 GPIO 开关,三路比较器输出接三个输入 GPIO,alignment_mode指定比较器方案。
switch_config_t_t upper_switch_config = { .control_type = CONTROL_TYPE_LEDC, .bldc_ledc = { .ledc_channel = {0, 1, 2}, .gpio_num = {17, 16, 15}, }, }; /* ... 省略:lower_switch_config 下桥臂 GPIO(12/11/10) 配置 ... */ bldc_zero_cross_comparer_config_t zero_cross_comparer_config = { .comparer_gpio[0] = { .gpio_num = 3, .gpio_mode = GPIO_MODE_INPUT, .active_level = 0 }, .comparer_gpio[1] = { .gpio_num = 46, .gpio_mode = GPIO_MODE_INPUT, .active_level = 0 }, .comparer_gpio[2] = { .gpio_num = 9, .gpio_mode = GPIO_MODE_INPUT, .active_level = 0 }, }; bldc_control_config_t config = { .speed_mode = SPEED_CLOSED_LOOP, .control_mode = BLDC_SIX_STEP, .alignment_mode = ALIGNMENT_COMPARER, /* 关键:比较器过零检测 */ .six_step_config = { /* ... 上下桥臂配置 ... */ }, .zero_cross_comparer_config = zero_cross_comparer_config, }; bldc_control_handle_t handle = NULL; bldc_control_init(&handle, &config); bldc_control_set_dir(handle, CCW); bldc_control_start(handle, 300); /* 300 RPM 目标启动 */三个 API 就是全部交互入口。跑起来之后你会发现,组件自己会把 INJECT → ALIGNMENT → DRAG → CLOSED_LOOP 一路走完,你只需要盯着日志里的BLDC_CONTROL_CLOSED_LOOP事件。
为什么选硬件比较器而不是 ADC 采样
组件的alignment_mode_t枚举给了两条路:ALIGNMENT_ADC和ALIGNMENT_COMPARER。两者差别比想象中直接:
| 维度 | ALIGNMENT_ADC | ALIGNMENT_COMPARER |
|---|---|---|
| 过零信号形态 | 模拟电压,需软件判阈值 | 硬件比较器已输出 0/1 电平 |
| 采样时机 | 必须卡在特定 PWM 时刻 | 无强制同步要求 |
| CPU 负载 | ADC 转换 + 软件滤波 | 读 GPIO,一次读三相 |
| 硬件依赖 | ADC 通道 + MCPWM 时序配合 | 三路比较器 + 普通 GPIO |
一句话:比较器模式里"GPIO 电平即过零",CPU 侧只剩位操作级别的活,实时性和抗干扰都占便宜。本文后面只讲ALIGNMENT_COMPARER,ADC 方案见组件文档另行处理。
虚拟中性点是怎么"造"出来的
痛点先说清楚:BLDC 三相绕组多为 Y 型连接,中性点在板子上不引出,可过零判据偏偏是"相电压相对中性点"的穿越。怎么办?
- 三相端电压各串一个等阻值电阻,汇聚到一个公共点;
- 三个等阻值电阻的分压结果,让这个公共点电压近似等于真实中性点——这就是"虚拟中性点";
- 每相电压接比较器一个输入端,虚拟中性点接另一端,输出翻转的时刻就是该相反电势过零点。
原理到此为止,接下来看它怎么落到电路板上。
三路比较器接线要点
参考设计的硬件拓扑(下图):三个运放当比较器用,每相一路;第四路把 BEMF_1/2/3 各串 1K 再汇聚,构成 BEMF_MID——虚拟中性点在这里有了实体。
- 虚拟中性点网络:BEMF_1/2/3 各经 1K 接入第四路比较器输入,公共节点即 BEMF_MID;
- 分压限流:每相输入端用 20K/1K 电阻网络分压,把母线电压压到运放共模范围内;输出串 100R 限流;
- 滤波电容:输出端 100nF 对地,滤掉 PWM 开关沿的高频噪声,避免比较器输出抖成锯齿;
- GPIO 接入:BEMF_1/2/3 直接进 ESP32 普通 GPIO,
GPIO_MODE_INPUT,无其他约束。
排查提示:用示波器看到比较器输出抖成"心电图"时,先查输出端滤波电容,再查三相等值电阻的精度是否一致——虚拟中性点偏了,三路信号全跟着歪。
六步换相:状态 → 动作速查
先交代一个文档勘误:bldc_snls_comparer.rst 里映射表表头三列都写着 "ZERO U",结合源码中三相状态数组的用法,这三列实际对应 U/V/W 三相。下表按 U/V/W 呈现。
约定:电平 1 = 该相端电压高于虚拟中性点,0 = 低于。三相组合共 6 种有效状态,对应 6 个 60° 电角度扇区;正反转各一套换相序列。
正转(CW)
| # | ZERO U | ZERO V | ZERO W | U 相 | V 相 | W 相 |
|---|---|---|---|---|---|---|
| 1 | 0 | 0 | 1 | 上开下关 | 上关下开 | 上关下关 |
| 2 | 0 | 1 | 1 | 上开下关 | 上关下关 | 上关下开 |
| 3 | 0 | 1 | 0 | 上关下关 | 上开下关 | 上关下开 |
| 4 | 1 | 1 | 0 | 上关下开 | 上开下关 | 上关下关 |
| 5 | 1 | 0 | 0 | 上关下开 | 上关下关 | 上开下关 |
| 6 | 1 | 0 | 1 | 上关下关 | 上关下开 | 上开下关 |
反转(CCW)
| # | ZERO U | ZERO V | ZERO W | U 相 | V 相 | W 相 |
|---|---|---|---|---|---|---|
| 1 | 0 | 1 | 0 | 上关下开 | 上开下关 | 上关下关 |
| 2 | 1 | 1 | 0 | 上关下开 | 上关下关 | 上开下关 |
| 3 | 1 | 0 | 0 | 上关下关 | 上关下开 | 上开下关 |
| 4 | 1 | 0 | 1 | 上开下关 | 上关下开 | 上关下关 |
| 5 | 0 | 0 | 1 | 上开下关 | 上关下关 | 上关下开 |
| 6 | 0 | 1 | 1 | 上关下关 | 上开下关 | 上关下开 |
代码里,"电平组合 → 相位号"这一步由依赖库esp_sensorless_bldc_control_lib提供的zero_cross_comparer_get_phase()完成:输入滤波后的三相状态与旋转方向dir,返回 1~6 的相位号,越界值会累加error_hall_count,调试时它就是信号质量仪。换相动作序列是两张函数指针数组,定义在 bldc_six_step.c:six_step_comparer_cw_array[6]从UphaseH_VphaseL起按UphaseH_VphaseL → WphaseH_VphaseL → WphaseH_UphaseL → VphaseH_UphaseL → VphaseH_WphaseL → UphaseH_WphaseL循环,six_step_comparer_ccw_array[6]则是反序。bldc_six_step_turn()按control_param->dir选表、以相位号为下标取换相函数执行——每步只动两路管子,第三相保持浮空。
源码里的三个"防抖 + 补偿"机制
比较器信号好不好用,全看这三处代码。
同步采样:只在"安静时刻"读 GPIO
换相瞬间续流和开关噪声最猛,所以组件不在任意时刻读电平,而是把采样钩到 MCPWM timer 计数满的固定时刻:
bool IRAM_ATTR read_comparer_on_full(mcpwm_timer_handle_t timer, const mcpwm_timer_event_data_t *edata, void *user_data) { bldc_zero_cross_comparer_t *zero_cross = (bldc_zero_cross_comparer_t *)user_data; alignment_comparer_get_value(zero_cross); return false; }alignment_comparer_get_value()做的事只有一行思路:三相 GPIO 电平读进来,左移压进alignment_queue_value[i]这个 64 位滚动队列,历史采样全部留痕。
连续滤波:位掩码说了算
队列值与位掩码ZERO_CROSS_DETECTION_ACCURACY按位与:全 1 判高、全 0 判低,混在一起就记filter_failed_count并跳过本周期。掩码直接决定"连续多少次一致才算数":
0xFF→ 连续 8 次0xFFFF(默认)→ 连续 16 次0xFFFFFFFF→ 连续 32 次,组件上限
滤波通过之后还要凑够ZERO_STABLE_FLAG_CNT(默认 4)次稳定过零才允许进闭环,约等于"先观察两圈再说",防止强拖阶段的不稳定信号误触发换相。
提前换向补偿:记住这个 30°
反电势过零信号在理论换向点前约 30° 电角度出现,检测到零之后不能立刻换向。但软件滤波又引入了额外滞后,于是代码两头凑:
#define ZERO_CROSS_ADVANCE (8) /* 换向角度 = 180°/8 = 22.5°,注释要求 >= 6 */即filter_delay = speed_count / ZERO_CROSS_ADVANCE:把 180° 电半周期均分,取出一段作为换向延迟,同时因滤波滞后适当缩短。物理含义一句话——用"半个电气周期切 N 刀"的方式标定提前角。顺带一提,转速也是这么算的:COMPARER_RPM_CALCULATION_COEFFICIENT / speed_count,系数即ft / (2 × n × c) × 60(ft 为 100µs 计数频率、n 为极对数、c 为半周期计数值),结果再走一阶低通BLDC_LPF(..., 0.2),过快或小于SKIP_INVALID_SPEED_CALCULATION的计数直接丢弃。
三处机制都落在 bldc_zero_cross_comparer.c,宏定义集中在 bldc_user_cfg.h。
启动状态机全景
从bldc_control_start()到无感闭环,状态机(枚举control_status_enum_t,定义于 bldc_control_param.h)长这样:
| 状态 | 进入条件 | 在干什么 | 离开条件 |
|---|---|---|---|
| INJECT | 调用bldc_control_start()且INJECT_ENABLE生效 | 高频脉振注入定位初始相位(需 MCPWM + 电流采样) | 初始相位确定 |
| ALIGNMENT | INJECT 完成或未启用 | 定相绕组通电,转子锁到初始相位(ALIGNMENTNMS控制时长) | 定相时间到 |
| DRAG | ALIGNMENT 完成 | 六步升压升频强拖,RAMP_TIM_*/RAMP_DUTY_*斜坡 | 反电势足够、过零信号出现 |
| CLOSED_LOOP | 稳定过零 ≥ZERO_STABLE_FLAG_CNT次 | PID 速度闭环,每次有效换相驱动下一步 | 速度命令或故障 |
| BLOCKED / FAULT / STOP | 堵转保护触发 / 错误计数 / 主动停机 | 安全态,关断输出 | bldc_control_stop()或复位重启 |
注意INJECT是"可选加强项":默认配置INJECT_ENABLE(0)关闭时直接从定相开始,硬件没有母线电流采样电路就别强开。
🔧 参数速查 & 调参心法
先别急着调参,把这几个默认值记在脑子里:
| 宏 | 默认值 | 一句话作用 | 副作用 |
|---|---|---|---|
PWM_MODE | BLDC_MCPWM | PWM 生成方式(MCPWM/LEDC) | 切 LEDC 则INJECT与on_full采样钩子失效 |
ZERO_STABLE_FLAG_CNT | 4 | 连续 N 次稳定过零后进闭环 | 调小进闭环快但易被噪声带偏;调大启动变慢 |
ZERO_CROSS_DETECTION_ACCURACY | 0xFFFF | 连续 N 次同电平才判有效 | 调深更抗噪但换相延迟增大,需同步加大ZERO_CROSS_ADVANCE |
ZERO_CROSS_ADVANCE | 8 | 180° 均分份数,决定提前换向延迟 | 必须 ≥ 6;换向并非越提前越好,过大扭矩反而掉 |
POLE_PAIR | 5 | 电机极对数 | 填错转速算错,PID 跟着错 |
排障按"症状 → 先查 → 再调"走:
- 进不了 CLOSED_LOOP→ 先查
filter_failed_count是否暴涨、比较器输出在示波器上是否干净;再确认PWM_MODE与INJECT配置匹配、ZERO_CROSS_DETECTION_ACCURACY是否还能加深; - 闭环后抖动 / 丢步→ 先查
error_hall_count是否持续增长(无效相位 = 信号质量问题);再加深滤波位掩码并同步加大ZERO_CROSS_ADVANCE; - 换向角度偏→ 先查示波器:实测换相沿相对反电势过零沿的实际提前角;再小步调
ZERO_CROSS_ADVANCE,一次只动一档; - 电机方向反了→ 先查
bldc_control_set_dir()的 CW/CCW 与电机实际绕线方向;换相表本身没错,错的多是方向参数。
强拖参数(RAMP_TIM_STA/END/STEP、RAMP_DUTY_STA/END/INC)务必在实际负载工况下调,空载跑通的参数上负载大概率翻车。
相关文件索引
- components/motor/esp_sensorless_bldc_control/user_cfg/bldc_user_cfg.h —— 全部用户可调宏
- components/motor/esp_sensorless_bldc_control/control/bldc_zero_cross_comparer.c —— 比较器过零核心逻辑
- components/motor/esp_sensorless_bldc_control/control/bldc_six_step.c —— 六步换相序列与执行
- components/motor/esp_sensorless_bldc_control/bldc_control.c —— 组件入口与状态机
- components/motor/esp_sensorless_bldc_control/test_apps/bldc_comparer/ —— 比较器模式测试工程
- docs/zh_CN/motor/bldc/bldc_snls_comparer.rst —— 官方比较器方案文档
- docs/zh_CN/motor/bldc/esp_sensorless_bldc_control.rst —— 组件完整使用指南
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考