B-G431B-ESC1 这个板子我用了挺长一段时间,前阵子一个同行发来一个问题:电机没接、动力电也没上,打开 Motor Pilot 看电流波形,Iq 直接冒出来一个不小的数值,问是不是板子的采样电阻烧了。我让他先别急,这类现象在 FOC 调试里太典型了——不是硬件坏了,多数情况下是零点没校准。这篇文章就把我排查这个问题的完整链路、背后的原理、以及最终的修复方法好好梳理一遍,给后面踩到同一个坑的人一个参考。
1. 先别怀疑硬件:Iq 非零的物理背景与现象边界
1.1 确认你看到的是“哪个 Iq”
先说个容易被忽略的点:界面里显示的 Iq 到底是以什么单位、从哪个环节读出来的?Motor Pilot 默认的电流观测窗口里,你可以选择看实际电流(单位 A),也可以看经过标幺化之后的内部数值(比如百分比或 Q 格式),甚至可以直接看 ADC 原始码。同样是“Iq 非零”,含义完全不同。
我一般拿到这种问题,第一件事不是打开原理图,而是让对方把 Motor Pilot 的截图上发来。很多时候,界面上显示的所谓“Iq”其实是内部变量Iq的标幺化表示。在做 FOC 时,内部电流环的基准值和反馈值通常是 Q15 或 Q24 格式的定点数,0 这个点在内部并不一定对应 0 电流。如果你看的是原始内部变量,那么一个非零的Iq可能只是定点数换算偏差,甚至就是一两百个码值,换算成实际电流只有几十毫安,根本不值得一提。
真正需要警惕的是:单位明确是安培(A),显示值却有 0.5A 甚至更高。这时候才说明电流采样链路里有东西不对。
1.2 未接电机为什么还能“测”到电流
很多初学者会把“电流采样”理解成一个简单的测量过程:有电流就有读数,没电流读数就是零。但在 FOC 的硬件设计里,电流采样电路始终是上电工作的,它不会因为电机没接而“睡着”。
B-G431B-ESC1 这类板子的电流检测结构是:低侧采样电阻(shunt)把相电流变成毫伏级电压,再经过运放放大,进入 STM32G431 内部的 ADC。G431 内置了三路可编程运放,正好配合三电阻采样方案。关键问题来了:运放是单电源供电的,只能输出 0~3.3V 之间的电压,而相电流是双向的(正负都会流)。如果 0 电流时运放输出 0V,那负电流就没办法表示了。
所以电路设计上必须给运放设定一个直流工作点,通常就是 VREF/2,也就是 1.65V。也就是说,无电流时,运放输出不是 0V,而是 1.65V。这个电压进入 ADC 之后,12 位采样得到的理想码值大约是 2048。这本身是正常的,但如果你在软件里没有把“2048”这个基准扣掉,而是直接把 2048 当成电流换算的起点,那结果就会是一个巨大的虚假电流。
可以用一个生活化的类比:这就跟电子秤一样,空秤状态下如果没有先做去皮(tare),上面放一个空箱子也会显示 10 公斤。你这时候不能说称坏了,而是没清零。
1.3 不校准的话,最大虚报电流能有多少
我习惯把这个误差量化出来,因为只有知道极限值,才能判断当前显示的数值是否落在“未校准偏移”的量级范围内。
换算公式很简单:
I_fake_max = (VREF / 2) / (Gain * Rshunt)其中 VREF 是 ADC 参考电压,通常是 3.3V;Gain 是运放增益;Rshunt 是采样电阻阻值。B-G431B-ESC1 的采样电阻具体值以板子原理图为准,不同批次可能有区别。我用两个典型参数算一下:
| 采样电阻 Rshunt | 运放增益 | 满量程电流范围 | 未校准时虚报电流 |
|---|---|---|---|
| 0.05 Ω | 16 | ±2.06 A | ≈ 2.06 A |
| 0.01 Ω | 10 | ±8.25 A | ≈ 8.25 A |
看到没有?如果完全不处理零点,Iq 直接显示一个接近满量程的电流值,而且这个值比很多小电机的额定电流还大。别说外行,有些做过一阵子驱动的人看到这个数,也会第一时间怀疑采样电阻烧了、运放坏了、甚至板子的地线有问题。实际上这只是“没做 offset 校准”的典型症状。
2. 从运放偏置到 Park 变换:Iq 究竟是怎么算出来的
2.1 电流采样链路盘点:电阻、运放和 ADC
要真正理解这个问题,得把从电流到 Iq 的完整信号链捋一遍。FOC 控制算法本身是不知道“电流”这个东西的,它能看到的只是 ADC 数字。整个链路是这样的:
相电流流过采样电阻 → 产生差分电压 → 运放按固定增益放大 → 加上直流偏置 → ADC 采样得到 12 位码值 → 软件换算成实际电流值 → 经过 Clarke 变换得到 Ialpha/Ibeta → 经过 Park 变换得到 Id/Iq。
任何一个环节的误差都会反映到最后 Iq 上。在没有接电机、没有动力电的情况下,采样电阻上不可能有真实的相电流,但运放输入端的共模电压还在,运放本身的失调电压也还在。换句话说,链路的前半段依然在输出一个“有意义的电压信号”,只是这个电压不代表电流。
经常被忽略的一点是:G431 内置运放的增益并不是无限可调的,它有几档固定的增益配置,比如 2、4、8、16、32 倍(具体型号略有差异)。MC Workbench 生成工程时,会根据你填的采样电阻和最大电流值自动选增益。如果这块配置和实际硬件对不上,也会导致电流读数成比例偏大或偏小。
2.2 单电源运放的“1.65V 谜团”
为什么偏偏是 1.65V?因为 ADC 的参考电压是 3.3V,而电流需要双向测量,所以电路设计者会把零电流点放在量程正中间。这样一来:
- 正电流 → 运放输出高于 1.65V
- 零电流 → 运放输出约等于 1.65V
- 负电流 → 运放输出低于 1.65V
这个 1.65V 就是“零电流偏置”。ADC 采样到 1.65V 时,码值是 2048。软件必须记录这个 2048,并且在之后每一次读取 ADC 时都先减掉它,再去做电流换算。
问题往往出在这里:如果初始化阶段没有执行零点采样,或者采样的时机不对,adc_offset变量就是 0。那么下一次实时电流换算时:
I = (raw_adc - 0) * resolution2048 这个码被当成真实电流信号,算出来的当然不是 0。实测下来,如果运放增益和采样电阻的乘积比较大,这个虚假电流甚至会触发过流保护,导致电机根本启动不了——这是比“Iq 显示非零”更严重的连锁反应。
2.3 Clarke/Park 变换对偏移量做了什么
那么问题来了:既然三相采样电路基本对称,各路的零点偏移都接近 2048,经过 Clarke 变换后两个偏移量相减,是不是就抵消了?
理论上,如果三相 ADC 的偏移量完全一致,Clarke 变换确实能消除共模偏移。但现实中不可能完全一致:三路运放的失调电压不同,三路采样电阻的阻值有误差,ADC 各路通道的偏置也略有差异。所以经过 Clarke 变换后,Ialpha 和 Ibeta 上会残留一个很小的直流分量。
Park 变换呢?在电机未启动、状态机处于 IDLE 阶段时,转子电角度通常是一个固定值或无效值。很多实现里此时角度保持为 0,而 Park 变换在 θ=0 时的表达式是:
Iq = -Ialpha * sin(0) + Ibeta * cos(0) = Ibeta也就是说,这时候 Iq 基本上直接等于 Ibeta 采样残差。如果 Ibeta 因为零点偏置没消干净,Iq 自然就显示非零。这也就解释了为什么很多人只盯着 Iq 看,因为在界面里 Iq 是控制环最关心的量,最容易被关注到。实际上此时 Ialpha、Ibeta 同样非零,只是没那么显眼。
3. 完整排查链路:顺着显示值一路往根上挖
3.1 第一步:冻结现象,看清单位与变化规律
拿到问题后,我先让他做这样几件事:
确认 B-G431B-ESC1 通过 USB 供电,ST-LINK 的 3.3V 正常输出。之所以强调这点,是因为如果连控制电都没接,MCU 根本不会工作,更谈不上显示 Iq。所以“没接 power”指的是没接主回路动力电,板子的控制电路必须在工作。
在 Motor Pilot 里停掉电机(其实本来就没启动),打开电流观测窗口,记录 5 秒钟的 Iq 波形。
观察这个非零值是恒定直流,还是有规律波动。
这一步的区分意义很大:如果 Iq 是一条水平直线,大概率是零点偏移问题;如果 Iq 随角度在 sin 曲线附近波动,那可能是有外部干扰或者角度信号本身在更新;如果是杂散噪声,量级又很小,这就纯属正常,不需要处理。
3.2 第二步:抓原始 ADC 码,把“电压”和“电流”分开看
这一步是判断问题在硬件还是软件的关键。用调试器(比如 ST-LINK 自带的 SWD 接口)连接板子,直接读取 ADC 数据寄存器,或者把 MC Workbench 生成的工程里电流采样函数返回的原始ADC_DR值打出来。
重点看三个东西:
- 三路 ADC 原始码是否都在 2048 附近;
- 三路之间的差异是否在几十个码以内;
- 把板子静止放置几秒,码值是否稳定。
如果三路都接近 2048,说明运放和 ADC 硬件工作正常,问题百分百在软件换算端——没有正确扣 offset。如果某一路偏差特别大,比如只有 1500 或者 2500,那才需要怀疑那一相采样电阻虚焊、运放增益配置错误、或者 PCB 走线干扰。
我当时让同行读出来的三路 ADC 码分别是 2042、2051、2047,非常标准的三路均偏。这就直接排除了硬件故障,锁定了软件 offset 环节。
3.3 第三步:核对 SDK 生成的校准流程是否执行
B-G431B-ESC1 搭配 STM32 Motor Control SDK(MC Workbench 生成工程)使用时,偏移校准是内置状态机的一部分。正常情况下,在电机启动之前、主回路 PWM 还没有输出的时候,SDK 会先执行一次偏移校准,采样若干次 ADC 值取平均,存成内部偏移量。
需要检查的两个点:
一是确认 MC Workbench 工程里电流采样配置是否选对了采样方式。如果是三电阻采样,那三路 ADC 的触发必须和 PWM 同步,并且在同一个时刻一起采样。
二是确认工程代码里有没有被改过。很多人在调试时会手动加代码、屏蔽初始化流程,可能把校准这段状态机跳过了。SDK 的状态机里,MCI_CalibrateCurrent()这类操作(名称可能随 SDK 版本不同略有差异)必须保持在启动流程中。如果被注释掉或者被用户自己的初始化代码覆盖,就会出现“未校准直接开跑”的情况。
3.4 第四步:检查校准时序是否被破坏
还有一个隐蔽的坑:校准函数确实执行了,但执行的时机不对。偏移校准的逻辑是“在当前时刻所有采样点都处于零电流状态的前提下,把 ADC 输出记录为基准”。如果校准那一刻 PWM 已经启动,电机绕组里已经有电流在流动,那么这次校准采到的“基准”里混入了真实电流。
更典型的场景是:电机启动后,你手动触发了一次重新校准。这相当于把正在运行的电流当成了零点,后面的 Iq 输出会出现一个巨大的直流偏置。校准只能在零电流状态下做,这个前提一旦打破,校准结果就是垃圾。
另外提醒一句:如果电机接了但没接电源,你在校准时顺手把电机轴转了一下,电机处于发电机状态,绕组里会产生反电动势电流,同样会污染校准结果。所以标准做法是:校准期间 PWM 关闭、低侧桥臂不导通、电机轴完全静止。
4. 修复与验证:把零点校准真正跑通
4.1 首选:使用 Motor Control SDK 自带的校准流程
对于 B-G431B-ESC1 的用户,我最推荐的做法是不要自己重新发明轮子,直接用 SDK 的标准校准流程。具体操作如下:
用 MC Workbench 重新打开工程,检查“Current Sensor”页面中的参数:采样电阻值、运放增益、最大电流范围。
确认这些参数和板子原理图一致。这一步非常关键,因为 SDK 会根据这些参数自动计算电流换算系数,如果参数填错,即使偏移校准正确,显示的电流值也会成比例偏移。
重新生成工程代码,保持默认初始化顺序不变,确保校准状态机在 PWM 使能之前完成。
编译烧录后,不接电机、不接动力电,用 Motor Pilot 连接,观察 Iq 读数。
正常情况下的读数应该在 ±0.02A 以内,甚至更小。这个数值取决于 ADC 的量化噪声和运放本身等,几十毫安的抖动是可接受的。如果稳定显示在 0 附近,故障就排除了。
4.2 备选:从零搭建 FOC 时手动实现 offset 校准
如果你没用 SDK,而是自己从零写 FOC 控制代码,那偏移校准完全要靠自己实现。原理很简单:在 PWM 输出前,对每路 ADC 连续采样多次取平均值,存为偏移值。之后每次换算电流时都先减掉这个偏移。
下面是一个可以直接参考的实现思路:
#define ADC_FULL_SCALE 4096u #define ADC_VREF_V 3.3f #define ADC_OFFSET_SAMPLES 256u /* 假设采样电阻为 0.01Ω,运放增益为 10 */ #define SHUNT_RESISTOR_OHM 0.01f #define OPAMP_GAIN 10.0f static uint16_t ia_offset, ib_offset, ic_offset; void current_offset_calibration(void) { uint32_t sum_a = 0, sum_b = 0, sum_c = 0; /* 校准条件:PWM 尚未使能,电机处于零电流状态 */ for (uint32_t i = 0; i < ADC_OFFSET_SAMPLES; i++) { sum_a += adc_read_a(); sum_b += adc_read_b(); sum_c += adc_read_c(); } ia_offset = (uint16_t)(sum_a / ADC_OFFSET_SAMPLES); ib_offset = (uint16_t)(sum_b / ADC_OFFSET_SAMPLES); ic_offset = (uint16_t)(sum_c / ADC_OFFSET_SAMPLES); } float phase_current_a(uint16_t raw_adc, uint16_t offset) { float resolution = ADC_VREF_V / (ADC_FULL_SCALE * OPAMP_GAIN * SHUNT_RESISTOR_OHM); int32_t code = (int32_t)raw_adc - (int32_t)offset; return (float)code * resolution; }这段代码里有两个容易踩的细节:
第一,采样次数不能太少。单次采样噪声太大,尤其是开关电源纹波耦合到模拟电路上时,单次采样值可能偏差几十个码。取 256 次平均能把随机噪声压到比较低。
第二,必须确保校准期间 PWM 是关闭的。如果校准函数在 main 初始化阶段就被调用,此时定时器还没配置,PWM 自然也没输出,这个条件通常满足。但如果在校准函数之前就初始化了 PWM,而你在初始化里把输出比较通道配置为立即输出高电平,那低侧桥臂可能已经导通,电流回路就建立了。
4.3 验证校准效果的三个标准
校准完不要只看 Iq 是 0 就算完,我每次都会做三组验证:
第一组:不接电机,只给控制电,看三相电流采样值是否都在 0 附近,且三相之和接近 0。如果三相电流和明显不为 0,说明偏移没扣干净,或者三路 ADC 协调采样有问题。
第二组:用示波器测运放输出引脚,确认无电流时各运放输出都在 1.65V 左右,且没有明显的高频纹波。如果纹波大,要检查模拟地和功率地的分割,或者 ST-LINK 供电时的开关噪声。
第三组:接上电机但依然不接动力电,用手缓慢转动电机轴。此时因为电机是永磁体,转动会产生反电动势和很小的电流,Iq 应该跟随转速发生变化,但变化量级应该在几十毫安以内,而且停止转动后回到 0 附近。如果手不转了 Iq 还停在一个偏置上,那说明还有零漂没消除。
5. 这个坑教我的排查方法论
5.1 看电流之前先问三个问题
经历这件事之后,我给自己定了个规矩:任何人拿“电流显示异常”来问,我先让他回答三个问题——
- 现在显示的是什么单位?是安培,还是 ADC 码,还是内部定点数?
- 这个电流有没有形成物理回路?如果电机没接、主回路没电,那就不可能有真实电流,怀疑采样和换算。
- 换算公式里的偏移和增益是否经过校准?如果 offset 是 0 或者用的默认值,那非零显示几乎是必然的。
这三个问题能过滤掉至少一半的“假故障”。很多时候问题根本不在板子,而在软件对“零”的定义。
5.2 相似现象的快速类比
这个“没接电机却显示 Iq 非零”的问题,和下面几类现象本质是同一个根源:
- 刚上电就报过流故障,电机完全没动。这是最危险的一种,因为过流保护会把 PWM 永久关闭,导致你查来查去以为驱动电路有问题。
- 电机的三相电流观测值总是偏向某一个方向,比如 Ia 始终偏大。这往往是某一路 ADC offset 校准不完整,或者运放增益配置不一致。
- 电机低速运行时电流波形有明显的直流偏置,速度越高越不明显。这个偏置就是零点偏移没扣干净,速度高时相电流幅值大,直流偏置被“淹没”了,但低速时特别扎眼。
如果你遇到的是这几类问题,排查思路完全复用:先回到零电流状态校准零点,再谈其他。
5.3 把“零电流基线”写进调试习惯
最后分享一个我自己的习惯:每次拿到一块新板子,烧录完固件之后,第一件事不是接电机,而是先把板子放在桌上,不接动力电,打开电流观测窗口记录一段基线波形。这个基准值我会截图存下来,之后任何电流异常都以这个基线为参照。
为什么强调这个?因为同一块板子在不同温度、不同供电电压下,ADC 偏移是会漂移的。1.65V 这个零点会随着运放的温漂慢慢移动。如果你只在校准那一刻记了一个固定偏移,长期运行后难免会出现很小的零漂。有了基线,你至少能分辨某个非零 Iq 是“偏移没扣干净”还是“真实电流”,不至于一上来就怀疑硬件。
电机控制是个很吃经验的领域,很多“灵异现象”说到底都是信号链路上某个基准点没弄对。B-G431B-ESC1 这个板子本身的硬件质量是很可靠的,遇到 Iq 非零先按我上面这条链路查一遍,比盲目换板子、换电机有效得多。也希望你在自己的项目里碰到类似问题时,第一反应不是“硬件坏了”,而是“零点有没有被正确清零”。