esp_sensorless_bldc_control 比较器反电势过零检测与六步换相源码解读
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
这篇文章以 esp-iot-solution 仓库中的esp_sensorless_bldc_control组件为对象,讲清基于硬件比较器检测反电动势(BEMF)过零点的无感方波电机控制方案:先给出过零信号的物理来源与虚拟中性点技巧,再走读三路比较器硬件电路,然后按"采样、滤波、测速、状态映射"四个机制逐段读源码,最后覆盖接线、bldc_user_cfg.h配置与闭环启动的完整流程,并附上典型故障现象的排查对照。跟着走读完成后,你可以基于仓库自带测试用例独立完成比较器三路接线、参数调整与无感闭环启动。
一、快速总览
| 条目 | 内容 |
|---|---|
| 核心概念 | 比较器输出过零电平判断转子位置 |
| 硬件门槛 | 三路比较器、等阻网络、三个输入 GPIO |
| 适用场景 | 低成本无感方波驱动、无霍尔电机 |
| 关键文件 | comparer.c 与 six_step.c |
该方案是组件内与ALIGNMENT_ADC(ADC 采样)并列的另一种无感模式,通过配置项ALIGNMENT_COMPARER启用。两者共用同一套六步换相框架,区别只在过零信号的产生与确认方式。
二、原理走读
过零信号从哪来:反电势幅值与转速成正比
方波驱动的 BLDC 电机任意时刻只有两相导通(一相接正、一相接负),第三相浮空。浮空相绕组没有电流,其端电压主要就是反电动势,因此浮空相是检测转子位置的最佳窗口。
反电势的物理来源是绕组切割转子磁场,按楞次定律其极性总是阻碍电流变化:
BEMF = NlrBω| 符号 | 含义 |
|---|---|
| N | 绕组匝数 |
| l | 转子导条有效长度 |
| r | 转子内半径 |
| B | 转子磁场强度 |
| ω | 转子角速度 |
电机下线后 N、l、r、B 都是定值,所以反电势幅值只与角速度 ω 成正比。这直接决定了两个工程事实:转速越快过零检测越可靠;转速过低时反电势弱到淹没在噪声里,必须先靠对齐(ALIGNMENT)和强拖(DRAG)把转速拉起来,过零信号才稳定可用。
图中每个 60° 电角度发生一次换相,换相后新浮空相的反电势在半周期内穿越中点,这个穿越点就是比较器要捕捉的过零点。
关键技巧:等阻电阻重构虚拟中性点
判断过零需要比较"端电压与中性点电压谁大",但星形接法的电机通常不引出中性点,无法直接测量。比较器方案的做法是用相同阻值的电阻把三相拉到公共点,该公共点电压近似等于真实中性点,称为虚拟中性点。每相端电压与虚拟中性点各接一路比较器的输入,比较器输出高低电平即代表该相端电压在不在中性点上方,电平翻转时刻就是过零点。
过零间隔与转速的数学关系
浮空相反电势每半个电气周期(180° 电角度)穿越一次中点。设计时基准频率为 ft、电机极对数为 n、两次过零之间累计的计数值为 c,则:
speed_rpm = ft / (2 × n × c) × 60ft 是周期中断频率,2×n 对应一个机械转内含有的半电气周期数。按组件默认配置MCPWM_CLK_SRC=20MHz、MCPWM_PERIOD=2000,计时周期ALARM_COUNT_US=100µs,POLE_PAIR=5,代入后系数为 60000,即speed_rpm = 60000 / speed_count。
三、硬件/接口走读
下左图是组件文档给出的功率侧参考:三相全桥(UH/VH/WH 上管、UL/VL/WL 下管)驱动星形绕组,母线 Udc 加在桥臂之间。下右图是信号侧:每相端电压经 1K 分压电阻(R25/R28、R29/R31、R32/R34)接入比较器反相端,等阻 20K 电阻(R25 上方标注 20K 的网络)合成虚拟中性点接比较器同相端,输出经 100R + 100nF 滤波后分别得到 BEMF_1/2/3 三路过零信号,另有一路比较器(U8:2)用于输出端分压参考。
硬件要点:
- 三路比较器支路:每相一路,负责把该相端电压与虚拟中性点的比较结果变成数字电平,是过零信号的直接来源;排查时先用示波器确认三路输出在电机转动时确有规律翻转。
- 等阻虚拟中性点网络:R28/R29/R32 等电阻必须同阻值,任一路偏差都会把中性点基准拉偏,导致某一相过零点系统性偏移。
- 输出 RC 滤波:100R 串联加 100nF 对地,抑制 PWM 开关噪声;电容过大则电平翻转滞后,与软件滤波叠加会加大换相延迟。
- GPIO 接口:三路信号接普通 GPIO,组件按输入模式初始化,
active_level字段用于适配比较器输出极性。
硬件排查顺序建议:先测三路比较器输出是否随转速稳定翻转,再核对等阻电阻的实测偏差(建议 1% 以内),最后检查输出端滤波电容取值与比较器供电、地线是否干净。
四、源码机制走读
同步采样:MCPWM on_full 时刻的 64 位移入队列
做什么:MCPWM 定时器每次计数到峰值(on_full 事件)时,把三路比较器 GPIO 电平各取一次,逐位移入每相 64 位环形队列。
为什么:换相刚发生的窗口内,端电压受续流二极管和开关噪声影响严重,连续读 GPIO 会采到大量脏数据。绑定 PWM 峰值时刻采样,保证每个 PWM 周期只取一个干净的样本点,同时把采样节拍与 PWM 严格同步。
在哪做:bldc_control.c 中注册回调,bldc_zero_cross_comparer.c 中实现采样:
// bldc_control.c: ALIGNMENT_COMPARER 分支 control->zero_cross = &bldc_zero_cross_comparer_operation; control->cbs->on_full = &read_comparer_on_full; /* 关键:PWM 峰值时刻触发采样 */ config->six_step_config.upper_switch_config.bldc_mcpwm.timer_cb_user_data = (void *)control->zero_cross_handle;// bldc_zero_cross_comparer.c: 每相 64 位队列左移 1 位并入当前电平 static void IRAM_ATTR alignment_comparer_get_value(bldc_zero_cross_comparer_t *zero_cross) { for (int i = 0; i < PHASE_MAX; i++) { zero_cross->alignment_queue_value[i] = zero_cross->alignment_queue_value[i] << 1; zero_cross->alignment_queue_value[i] |= gpio_ll_get_level(&GPIO, zero_cross->alignment_pin[i]); } }怎么调:采样时刻固定在 on_full,无独立参数可调;采样密度由MCPWM_CLK_SRC与MCPWM_PERIOD决定(默认 10kHz PWM),改 PWM 频率时注意队列窗口覆盖的时间随之变化。
连续滤波与过零确认:0xFFFF 相同样本判有效
做什么:把队列低 16 位与ZERO_CROSS_DETECTION_ACCURACY做按位与,全 1 判该相为高、全 0 判为低、否则判滤波失败并跳过本周期处理。
为什么:硬件滤波后仍可能有窄脉冲噪声,"连续多次同电平"的判决能拒绝孤立毛刺,避免把噪声当过零事件触发误换相。滤波失败时filter_failed_count累加,超过 15000 次会被主状态机判为堵转(BLOCKED),这一计数同时是信号质量的在线监测指标。
在哪做:bldc_zero_cross_comparer.c:
for (int i = 0; i < PHASE_MAX; i++) { if ((zero_cross->alignment_queue_value[i] & ZERO_CROSS_DETECTION_ACCURACY) == ZERO_CROSS_DETECTION_ACCURACY) { zero_cross->queue_filter_state[i] = 1; /* 连续 16 次高电平 */ } else if ((zero_cross->alignment_queue_value[i] & ZERO_CROSS_DETECTION_ACCURACY) == 0) { zero_cross->queue_filter_state[i] = 0; /* 连续 16 次低电平 */ } else { zero_cross->control_param->filter_failed_count++; return 0; /* 滤波失败:本周期不处理 */ } }怎么调:ZERO_CROSS_DETECTION_ACCURACY默认0xFFFF(连续 16 次相同),最大0xFFFFFFFF(32 次)。改大会提高抗噪能力,但电平确认延迟增大、换相整体后移,需与ZERO_CROSS_ADVANCE联动补偿。
测速与提前角补偿:speed_count 折算 RPM
做什么:以 U 相滤波状态为基准做边沿检测,检测到 1→0 下降沿时,把本半周期累计的speed_count折算成转速,经一阶低通后写回speed_rpm,同时算出提前换向延迟filter_delay。
为什么:无传感器方案需要转速反馈给 PID;而比较器检测到的过零点理论上是换向前 30° 电角度到来,加上软件滤波滞后,若立即换相角度会偏晚,需要按当前转速折算出延迟量做补偿。
在哪做:bldc_zero_cross_comparer.c,系数定义在 bldc_config.h:
#define COMPARER_RPM_CALCULATION_COEFFICIENT (((1000000 / ALARM_COUNT_US) / (2.0 * POLE_PAIR)) * 60) // bldc_zero_cross_comparer.c: U 相状态 1->0 下降沿处 if (filterEdge == 0) { if (zero_cross->zero_stable_flag >= ZERO_STABLE_FLAG_CNT) { speed_rpm = (uint32_t)(COMPARER_RPM_CALCULATION_COEFFICIENT / zero_cross->control_param->speed_count); BLDC_LPF(zero_cross->control_param->speed_rpm, speed_rpm, 0.2); /* 系数 0.2 低通平滑 */ } zero_cross->control_param->filter_delay = zero_cross->control_param->speed_count / ZERO_CROSS_ADVANCE; }怎么调:POLE_PAIR默认 5,必须按电机实际极对数设置,否则转速读数整体成倍偏差。ZERO_CROSS_ADVANCE默认 8,提前角 = 180°/该值(默认 22.5°,略小于理论 30° 以抵消滤波滞后),注释要求不小于 6;改小则换相更提前,过快会过冲,建议用示波器对比实际与理论换相角。
六种过零状态到换相动作的映射
做什么:滤波后的三相状态(各 0/1)共六种有效组合,zero_cross_comparer_get_phase()输入三相状态与方向dir,返回 1~6 的相位号;相位号变化时bldc_six_step_turn()按方向选择对应的六步数组执行换相。
为什么:三相比较器电平的组合恰好唯一编码了当前转子所在的 60° 扇区,查表即可把"电平组合"翻译成"下一步该导通哪两相",无需任何位置积分。
在哪做:相位判定在 bldc_zero_cross_comparer.c,换相序列与执行在 bldc_six_step.c:
// bldc_zero_cross_comparer.c: 连续稳定后取相位 uint8_t phase = zero_cross_comparer_get_phase(zero_cross->queue_filter_state, zero_cross->control_param->dir); if (phase <= 0 || phase > 6) { zero_cross->control_param->error_hall_count++; /* 非法组合:信号质量异常 */ return 0; }// bldc_six_step.c: 正转六步序列(函数指针数组) static void (*six_step_comparer_cw_array[6])(...) = { &bldc_six_step_UphaseH_VphaseL, /* U 上管 PWM,V 下管导通 */ &bldc_six_step_WphaseH_VphaseL, &bldc_six_step_WphaseH_UphaseL, &bldc_six_step_VphaseH_UphaseL, &bldc_six_step_VphaseH_WphaseL, &bldc_six_step_UphaseH_WphaseL, };方向数组的选择在 bldc_six_step.c:alignment_mode == ALIGNMENT_COMPARER时绑定比较器专用数组,否则绑定 ADC 数组。
官方文档中的映射表("1/0" 表示该相比较器输出高于/低于虚拟中性点;"上开下关 / 上关下开 / 上关下关" 表示该相桥臂状态):
正转(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 | 上关下关 | 上开下关 | 上关下开 |
说明:仓库文档 bldc_snls_comparer.rst 中两张映射表的表头三列均写作 "ZERO U",结合源码
queue_filter_state[PHASE_MAX]为 U/V/W 三相状态数组的事实判断,三列实际依次为 ZERO U、ZERO V、ZERO W,上表按此呈现。
怎么调:方向由bldc_control_set_dir(handle, CW/CCW)设置,方向设置必须与电机实际绕线一致,否则会反向或无法启动;调试时观察error_hall_count,持续递增说明三相状态长期落在六种合法组合之外,优先查硬件。
五、实践三步
第 1 步:添加组件依赖与用户配置文件
在项目根目录执行:
idf.py add-dependency "espressif/esp_sensorless_bldc_control=*"把 user_cfg/bldc_user_cfg.h 复制到项目的main/目录(保持文件名bldc_user_cfg.h),并在main/CMakeLists.txt中追加(与 test_apps/bldc_comparer/main/CMakeLists.txt 相同):
idf_component_get_property(bldc_lib espressif__esp_sensorless_bldc_control COMPONENT_LIB) cmake_policy(SET CMP0079 NEW) target_link_libraries(${bldc_lib} PUBLIC ${COMPONENT_LIB})验证点:idf.py build通过,且不再报bldc_user_cfg.h not found, must provide by user(该报错由 bldc_config.h 的__has_include检查触发)。
第 2 步:填写硬件配置结构体
参考 test_comparer.c,LEDC 驱动上管、GPIO 驱动下管的最小配置:
bldc_zero_cross_comparer_config_t zero_cross_comparer_config = { .comparer_gpio[0] = { .gpio_num = 3, .gpio_mode = GPIO_MODE_INPUT, .active_level = 0 }, /* U 相过零 */ .comparer_gpio[1] = { .gpio_num = 46, .gpio_mode = GPIO_MODE_INPUT, .active_level = 0 }, /* V 相过零 */ .comparer_gpio[2] = { .gpio_num = 9, .gpio_mode = GPIO_MODE_INPUT, .active_level = 0 }, /* W 相过零 */ }; bldc_control_config_t config = { .speed_mode = SPEED_CLOSED_LOOP, .control_mode = BLDC_SIX_STEP, .alignment_mode = ALIGNMENT_COMPARER, /* 关键:选择比较器过零检测 */ .six_step_config = { .lower_switch_active_level = 0, /* 关键:下管有效电平,须与驱动板一致 */ .upper_switch_config = upper_switch_config, .lower_switch_config = lower_switch_config, .mos_en_config.has_enable = false, }, .zero_cross_comparer_config = zero_cross_comparer_config, };验证点:comparer_gpio[i].gpio_num与硬件上 BEMF_1/2/3 的接线一一对应;active_level与比较器输出极性匹配(反接时软件滤波会一直判失败)。
第 3 步:初始化、设方向并启动
bldc_control_handle_t bldc_control_handle = NULL; bldc_control_init(&bldc_control_handle, &config); /* 初始化硬件驱动 */ bldc_control_set_dir(bldc_control_handle, CCW); /* 设置旋转方向 */ bldc_control_start(bldc_control_handle, 300); /* 以 300 RPM 启动 */验证点:串口日志依次出现BLDC_CONTROL_ALIGNMENT、BLDC_CONTROL_DRAG,稳定运行后打印CLOSED_LOOP事件(事件名表见 bldc_control.c);只停在 DRAG 不进入闭环即说明过零信号未达标。
六、排障速查
| 现象 | 可能原因 | 对策 |
|---|---|---|
| 比较器 GPIO 长期无规律翻转,进不了 DRAG 后的闭环 | 采样支路断线、等阻电阻偏差大、比较器未供电 | 示波器逐一确认 BEMF_1/2/3 输出;核对 20K 等阻网络阻值一致性(建议 1% 精度) |
| 强拖正常但始终进不了 CLOSED_LOOP | 滤波判决长期失败,filter_failed_count高 | 先把ZERO_CROSS_DETECTION_ACCURACY调大验证;仍无效则查硬件滤波电容与active_level配置 |
| 闭环后运行抖动、偶尔丢步 | 滤波深度不足,窄脉冲噪声触发误换相 | ZERO_CROSS_DETECTION_ACCURACY从0xFFFF提到0xFFFFFFFF,并同步减小ZERO_CROSS_ADVANCE补偿延迟 |
| 电机反转或启动时抖一下再反着转 | dir与绕线方向不符,或某两相线序接反 | 对调 U/V 两相接线,或bldc_control_set_dir改方向,二者改其一 |
| 闭环后进入 BLOCKED(日志打印 BLOCKED) | filter_failed_count超 15000 被判堵转,信号持续无效 | 观察error_hall_count是否同步递增;递增则为信号质量差,按前两项排查硬件 |
| 转速读数明显偏离实际值 | POLE_PAIR设置错误,测速系数成倍偏差 | 按电机铭牌极对数重设POLE_PAIR,读数偏差与设置比应成反比 |
七、关键参数速查
以下宏定义在 user_cfg/bldc_user_cfg.h:
| 宏 | 默认值 | 作用 | 调整方向 |
|---|---|---|---|
ZERO_STABLE_FLAG_CNT | 4 | 连续稳定检测到多少个过零后进入无感闭环 | 偏大→进闭环更晚但更可靠;偏小→启动段误判风险高 |
ZERO_CROSS_DETECTION_ACCURACY | 0xFFFF | 连续多少次相同采样判为有效电平(16 次) | 偏大→抗噪强但换相延迟大,需配合减小ZERO_CROSS_ADVANCE;偏小→噪点易通过,filter_failed_count上升 |
ZERO_CROSS_ADVANCE | 8 | 提前换向分母,提前角 = 180°/该值,要求 ≥ 6 | 偏大→提前角变小、换相偏晚;偏小→换相过提前,角度过冲 |
POLE_PAIR | 5 | 电机极对数,决定测速系数 | 偏大→转速读数偏低;偏小→读数偏高,须按实际极对设置 |
RAMP_DUTY_STA/RAMP_DUTY_END | 20% / 40% | 强拖起始/结束占空比(DUTY_MAX为 1000) | 偏大→启动扭矩强但易过流;偏小→带载时拉不起来 |
RAMP_TIM_STA/RAMP_TIM_END | 600 / 90 | 强拖换相间隔的起/止值(单位:gptimer 节拍) | 偏大→启动平滑但时间长;偏小→快但带载易丢步 |
MCPWM_CLK_SRC/MCPWM_PERIOD | 20MHz / 2000 | PWM 时钟与周期(10kHz),决定ALARM_COUNT_US=100µs | 改变会同步改变测速系数与滤波窗口,需整体复核 |
SPEED_KP/SPEED_KI | 0.004 / 0.00015 | 速度闭环 PID 增益 | 偏大→跟踪快但易振荡;偏小→响应迟钝、带载掉速 |
八、延伸阅读
- bldc_overview.rst:BLDC 结构、逆变桥与有感/无感控制方式的总览对比。
- bldc_snls_comparer.rst:官方对比较器方案的完整说明,含本文两张映射表的原始出处。
- esp_sensorless_bldc_control.rst:INJECT/ALIGNMENT/DRAG/CLOSED_LOOP 各启动阶段的参数详解与调参提示。
- bldc_control.c:状态机主体、事件分发与回调注册位置。
- bldc_zero_cross_comparer.c 与 bldc_six_step.c:过零确认与六步换相的两段核心实现。
- test_comparer.c:LEDC + GPIO 与 MCPWM 两种配置的完整运行示例,可直接烧录验证。
比较器方案的核心链条很短:等阻网络重构虚拟中性点,比较器把端电压比较结果变成三路电平,64 位队列滤波确认过零,再查表映射成六步换相。实际调参时最关键的一条经验是:加深ZERO_CROSS_DETECTION_ACCURACY滤波后务必同步减小ZERO_CROSS_ADVANCE,并用示波器核对实际换相角与理论换相角,换相角度并不是越提前越好。
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考