ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FOC无刷电机电角度自动校准:磁编码器与极对数获取实战

FOC无刷电机电角度自动校准:磁编码器与极对数获取实战 搞过FOC的朋友应该都清楚启动那一刻最难受的不是电流环没调好而是你还不知道转子的电角度到底在哪。拿AS5047P这类磁编码器做位置反馈时你读到的其实只是机械角度而FOC的Park变换需要的是电角度。这个角度对不上电流环算出来全是乱的轻则电机嗡嗡响重则直接反转或者过流烧管子。我以前手动校准电角度时拿着电机的三相线反复换相、锁轴、读编码器至少得折腾十几分钟换一台电机又得重来一遍。后来我把“机械角度与电角度的自动对齐校准”和“电机极对数自动获取”做成了一套固件里的自动化流程从此换电机调试基本就是一键的事。这篇文章就把这套方法的原理、算法、代码细节和调试中踩过的坑一次讲透。1. 校准思路与整体设计1.1 这个项目要解决什么问题FOC矢量控制的所有计算都建立在“当前转子电角度已知”这个前提上。坐标变换把三相定子电流从静止坐标系变换到与转子同步旋转的dq坐标系靠的就是电角度。如果这个角度有误差dq轴解耦就会失效你以为是给d轴加励磁电流实际上有一部分励磁分量跑到了q轴上产生额外力矩你以为是纯q轴电流输出力矩实际上又引入了不必要的d轴电流。结果就是电机力矩波动、效率下降严重时整个控制系统直接发散。磁编码器、霍尔、反电势观测器都能提供位置信息但它们的输出形式不太一样。霍尔只给出六个扇区信号分辨率很低反电势观测器在零速和低速下无法工作而AS5047P这类磁编码器可以直接读到一个14位的绝对机械角度上电就知道转子在哪个位置。看似方便但它和电机定子线圈之间的空间关系并不能直接拿到必须经过一次校准。换句话说编码器把机械角度给你了但你要自己把“机械角度”翻译成“电角度”。这个翻译过程就是本文要讲的校准。这里还有一个非常容易忽略的问题不同电机的极对数不同有的电机是2对极有的7对极甚至14对极。如果你把极对数写错哪怕电角度偏移校正对了运行中依然会出问题因为电机每转一圈电角度实际变化了p个周期而你只按1对极处理反馈角度和真实角度差得越来越离谱。所以一套完整的自动校准流程必须把极对数的自动获取也一起做掉。1.2 为什么选择磁编码器做FOC位置反馈无刷电机的转子位置反馈方案无非就那几类霍尔、增量编码器、绝对式磁编码器、无感观测器。工业控制里很多场合直接用增量编码器配Z信号做校准但Z信号需要电机转一圈才能找到而且增量编码器没有掉电记忆。AS5047P这类磁编码器不一样它是绝对式输出上电就能得到当前机械角度非常适合做电动工具、机器人关节、云台这类需要快速起动的设备。我在很多项目里都用AS5047P搭配STM32一方面是因为SPI接口读角度非常快一次读取大概几百纳秒到几微秒完全跟得上FOC的高频电流环另一方面是它最高支持14位分辨率也就是一圈分成16384份每份约0.022度对于大多数PMSM控制完全够用。实际使用上编码器的安装误差反而比分辨率更让人头疼。磁钢偏心、芯片贴歪、气隙不均匀都会造成角度读数有纹波。这些误差在校准过程中会被折算进电角度偏移里导致电机运行时出现周期性抖动。当然这种程度的误差在中低速控制场景下还可以接受实在不行就上软件补偿或换用校准后的传感器。1.3 自动对齐方案的总体设计从最终实现的固件来看这套自动校准流程分两大步先获取极对数再获取电角度偏移。这两步可以衔接得非常自然甚至可以在同一次“电角度扫描”中完成。核心思路很简单FOC控制器虽然还没校准但你仍然可以向电机注入一个确定的电流矢量只是你不知道这个矢量在真实转子坐标系里长什么样。没关系因为只要给一个足够大的励磁电流转子会被这个电流矢量拉过去然后稳定在一个确定位置。接下来你把电流矢量的方向缓慢旋转转子就会跟着转。在这个过程中编码器给出的机械角度变化量和你输入的电角度变化量之间就隐藏着极对数的信息。等极对数确定下来电角度偏移其实就更好办了。我们再回到某个已知的电流矢量方向让转子被拉过去并稳定住记录当时的机械角度。因为稳定时转子的真实磁极方向和电流矢量方向是重合的所以我们知道此刻的真实电角度等于我们施加的参考电角度。那么机械角度乘以极对数和这个参考电角度之间的差就是我们要的电角度偏移。是不是听起来很顺后面我会把每个步骤的坑都展开讲。2. 机械角度、电角度与极对数三个核心概念2.1 电角度不是“量出来”的是算出来的很多刚接触FOC的同学会误以为电角度直接来自传感器。其实不是。传感器只能告诉你机械角度电角度是机械角度乘以极对数再加上一个固定的偏移量。公式写出来就是θ_e p × θ_m θ_0其中θ_e电角度单位°或radp电机极对数θ_m编码器读到的机械角度范围0°~360°θ_0电角度偏移量主要取决于编码器安装角、电机相序标记和定子绕组布局。为什么会有这个偏移因为电机定子的A相绕组轴线、转子的初始磁极位置、编码器的零位这三者之间没有天然的对应关系。同一个机械角度在不同电机上对应的是不同的电角度甚至同一台电机重新拆装编码器后偏移量也会变。所以每台电机量产之前或者装机之后都值得做一次这样的校准。举个更直观的例子假设一台电机的极对数是7编码器读到的机械角度是60度偏移量经过校准是30度电角度那么此时真正的电角度就是θ_e 7 × 60° 30° 450°折算到0°~360°范围内就是90°。如果没算偏移直接拿420°当成电角度就会差出30°电角度低速可能不明显高速时电流相位就完全不对了。2.2 极对数的自动获取原理先看一个基本关系当我们给FOC注入的电角度指令从θ_ref增加到θ_ref Δθ_e时转子如果想跟随这个电流矢量它的机械角度需要增加Δθ_m。两者之间满足Δθ_e p × Δθ_m所以理论上极对数p就是“电角度变化量 ÷ 机械角度变化量”。问题在于电角度变化量是按“控制坐标系”来给的但如果你给的角度增量恰好超过360°电机就可能原地踏步或者出现多解。因此实际实现时我不建议把电角度增量一次给太大而是用一个小步长反复扫描累计一段时间后再算比值。实际操作中比较稳妥的做法是让电角度指令从当前值开始以固定步长步进每一步等待转子稳定后再继续。记录下电角度指令累计变化量以及编码器机械角度的累计变化量直到机械角度累计转过大约360°为止。此时如果电角度指令累计走了E度那么极对数就是p round(E / 360)打个比方电角度只有0.5度机械角度转了1度推不出极对数但如果电角度累计走了2190度机械角度也转了一整圈那极对数大约是6.08四舍五入就是6对极也就是12极电机。这里要特别说明一点摩擦力和负载转矩会影响转子能不能稳定跟随电流矢量。所以扫描过程中加入的励磁电流不能太小否则转子卡在某个角度不动就会导致计算出的极对数偏小。后面实战部分我会给出一个调试用的经验区间。2.3 AS5047P磁编码器在其中的角色AS5047P是AMS公司推出的一款14位磁编码器芯片内部集成了霍尔阵列通过检测磁钢的旋转磁场方向来计算角度。和传统光编码器相比它不担心灰尘油污结构上也更紧凑。FOC控制器通过SPI接口直接读取14位角度值一次读回来的数换算成机械角度就是uint16_t angle_raw AS5047P_ReadRegister(0x3FFF); // 读ANGLE寄存器 float theta_m (float)angle_raw * 360.0f / 16384.0f;这个值就是θ_m。在自动校准流程里我们既可以用它测量转子被电流矢量拉动后的稳态位置也可以在极对数扫描阶段实时累计它的变化量。这里有个小提醒AS5047P的原始角度在0°附近会从359.99°跳到0°如果你直接做差值会算出负的几百度的跳变。必须处理这种角度环绕否则极对数一算一个错。3. 算法实现从“手动对齐”到“一键自动校准”3.1 自动获取极对数的具体流程我把这段流程实现成了固件里的一个函数。总体结构如下先把电机置于电流闭环模式初始电角度设成0。给定id_ref I_align一个适合当前电机的励磁电流iq_ref 0让转子先锁到当前位置。等0.5秒让转子完全静止。记录当前机械角度作为起始位置。开始循环每次把参考电角度增加一个固定角度step比如15°或30°然后继续用id_ref I_aligniq_ref 0去控制。每次步进后等待转子稳定再读一次机械角度计算这次步进对应的机械角度增量并把增量累加到total_mech上。当total_mech已经接近或者超过360°时结束扫描。用累计的电角度变化量除以360四舍五入得到极对数。写成伪代码大致是这样float step 15.0f; // 电角度步进量单位度 float total_elec 0.0f; // 电角度指令累计变化量 float total_mech 0.0f; // 机械角度累计变化量 float theta_ref 0.0f; float theta_m_prev read_mech_angle(); set_current_vector(theta_ref, I_align, 0); // idI_align, iq0 wait_ms(500); while (total_mech 360.0f) { theta_ref step; if (theta_ref 360.0f) theta_ref - 360.0f; set_current_vector(theta_ref, I_align, 0); wait_for_settle(); // 等待转子稳定 float theta_m_now read_mech_angle(); float delta_m angle_delta(theta_m_now, theta_m_prev); total_mech delta_m; theta_m_prev theta_m_now; total_elec step; } int pole_pairs (int)(total_elec / 360.0f 0.5f);这里最关键的是等待转子稳定。我在调试里是取连续几次读取的角度差的绝对值如果连续50ms内这个差值都小于0.5°就认为转子已经稳定。步长选15°在大多数电机上都合适它既不会让转子跑飞导致失步又能在几十次步进后完成一整圈。如果电机极对数特别多比如7对极以上你会发现电角度指令需要走很多个周期才能让机械角度转一圈这没关系循环会自动多扫几轮直到机械角度累积到360°。3.2 自动获取电角度偏移的具体流程极对数确定之后我们再来算偏移量。最朴素的思路是既然电角度关系是θ_e p × θ_m θ_0我只要找到一个时刻能同时知道θ_e和θ_m就能反解θ_0。找一个“电角度已知”的时刻并不难。前面说过给一个参考电角度θ_ref再给idI_align、iq0转子最终会稳定在电流矢量方向上。此时真实的电角度就等于θ_ref准确说是和θ_ref同相位因为id0锁在稳定点。记录下机械角度θ_m那么θ_0 θ_ref - p × θ_m不过我不建议只取一个点。一次测量受摩擦、编码器噪声、电流纹波影响可能带来几度的误差。我实际使用的方法是取多个参考电角度比如从0°到359°等间隔取12个点每个点都用相同的方式把转子拉过去并稳定然后记录机械角度逐个计算偏移最后做圆形平均。代码如下#define N 12 float offset_sum 0.0f; float sum_sin 0.0f, sum_cos 0.0f; for (int i 0; i N; i) { float theta_ref (float)i * 360.0f / N; set_current_vector(theta_ref, I_align, 0); wait_for_settle(); float theta_m read_mech_angle(); float theta_e theta_ref; // 稳定后等于参考角 float offset theta_e - pole_pairs * theta_m; offset wrap_360(offset); sum_sin sinf(offset * M_PI / 180.0f); sum_cos cosf(offset * M_PI / 180.0f); } float avg_offset atan2f(sum_sin, sum_cos) * 180.0f / M_PI; avg_offset wrap_360(avg_offset);为什么用圆形平均因为偏移量是角度在360°边界上会跳变。比如有两个偏移1°和359°普通求平均是180°这显然是错的。圆形平均会先转换成sin和cos再做atan2得到0°这才是正确结果。3.3 校准前必须确认的方向与相序问题在跑自动校准之前有两件事必须确认否则后面全白干。第一件事是编码器的角度方向。把电机正转观察AS5047P读到的机械角度是增大还是减小。如果你的电机正转时角度反而减小那就要在程序里统一处理要么把机械角度取反要么反转方向标志位。方向不一致会导致后面所有计算都差一个负号表现就是电机运行中电流失控、噪声巨大。第二件事是三相相序。FOC的SVPWM输出定义了U、V、W三相但实际电机驱动器到电机绕组的接线只对应两两一组接反一相整个电流矢量方向就错了。有一个非常简单的检查办法A相通正电流、B相通负电流让转子被拉到一个位置然后换A正、C负观察转子是否按预期角度转动。如果转动方向不对就说明相序有误。不要嫌这一步麻烦自动校准只能在正确的硬件连接基础上工作。4. 实操过程与核心代码4.1 硬件连接与SPI读取我常用的硬件组合是STM32F405做主控DRV8301或类似栅极驱动芯片驱动三相全桥编码器用AS5047P磁钢直接装在电机转子轴端。AS5047P的接线很简单CS、SCLK、MOSI、MISO四根线接到STM32的SPI接口就行。注意AS5047P的SPI最高支持10MHz读取一个角度寄存器地址需要先写地址再读数据。下面这段代码是我在实际工程里的AS5047P读取片段uint16_t AS5047P_ReadRegister(uint16_t reg) { uint16_t tx (reg 0x3FFF) | 0x4000; // 命令位读操作 uint16_t rx 0; HAL_GPIO_WritePin(ENC_CS_GPIO_Port, ENC_CS_Pin, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(hspi2, (uint8_t*)tx, (uint8_t*)rx, 1, HAL_MAX_DELAY); // 第一个字节返回的是上一请求的结果所以需要再读一次 tx 0x0000; HAL_SPI_TransmitReceive(hspi2, (uint8_t*)tx, (uint8_t*)rx, 1, HAL_MAX_DELAY); HAL_GPIO_WritePin(ENC_CS_GPIO_Port, ENC_CS_Pin, GPIO_PIN_SET); return (rx 0x3FFF); } float get_mech_angle_deg(void) { uint16_t raw AS5047P_ReadRegister(0x3FFF); // ANGLE寄存器地址 return (float)raw * 360.0f / 16384.0f; }这里有个细节SPI读AS5047P时第一个16位时钟传回的数据实际上是上一次SPI请求的“结果”所以读一个寄存器需要再发一个空命令第二次收到的数据才是你这一轮想要的角度。如果你只发一次就读拿到的角度会滞后一拍在自动校准这种低速场景问题不大但在高速FOC里就会引入明显误差。4.2 FOC电流闭环与电流矢量设置自动校准的核心动作是“设置一个电流矢量”。严格来说这需要完整的电流环配合。在电机已经接好、母线电压正常、编码器能读的前提下我通常直接复用运行时的FOC电流环然后通过接口把电流参考值设置成固定id和固定iq。这里的设置方式等价于void set_current_vector(float theta_ref, float id_ref, float iq_ref) { current_ctrl_ref_id id_ref; current_ctrl_ref_iq iq_ref; current_ctrl_ref_theta theta_ref; }在常规FOC主中断里Park变换用的角度应该是当前编码器机械角度乘以极对数再加偏移。但在校准过程中偏移还是未知那就直接用这个theta_ref作为Park变换的角度。也就是说校准期间控制坐标系是“虚拟”的它虽然不代表真实转子电角度但是我们给电流环的参考值决定了施加到电机上的电流空间矢量的实际方向这就够了。需要注意的是给id_ref一个固定值相当于让电流环持续输出一个方向固定的电压矢量。如果这个值太大了电机会过流发热太小了又拉不住转子。我一般先把I_align设为额定电流的10%到15%然后观察转子是否稳定。比如一个额定电流10A的电机校准电流给1A到1.5A是比较合理的。4.3 自动校准主流程代码把上面所有逻辑整合到一起就是一个完整的自动校准函数。为了便于阅读我简化了一些无关紧要的外设细节void auto_calibrate_motor(void) { // 1. 极对数获取 int p estimate_pole_pairs(); pole_pairs p; // 2. 电角度偏移获取多点平均 float offset estimate_electrical_offset(p); electrical_offset offset; // 3. 写Flash保存下次上电直接用 save_calibration_to_flash(p, offset); }其中estimate_pole_pairs的内部实现就是上一节的伪代码。estimate_electrical_offset用多点遍历的方式实现。最后我会把这两个参数都存进STM32的Flash里这样重新上电后FOC直接使用不用每次都重新校准。实际运行这些代码时你会发现极对数的获取速度比想象中快。一个7对极的电机电角度步进15°机械角度每步大约2.14°要绕一整圈需要约168步如果每步等待50ms总体也就8秒多一点。这个时间对产线校准完全可接受。如果希望更快可以加大励磁电流、减少稳定等待时间但可靠性会变差。4.4 校准结果的验证校准不是跑完自动流程就结束了我每次都会做三个验证确保算法没算错。验证一静态保持。设置idI_align、iq0把校准后的电角度偏移代入FOC手动给定theta_ref让电流矢量对准转子d轴。这时候用手去拧电机轴应该感到明显的阻力就像伺服锁定一样。验证二开环斜坡。在位置开环模式下让电流矢量以固定速度旋转电机应该平稳转动编码器机械角度应该严格跟随指令角度除以极对数。如果出现一顿一顿的现象多半是偏移在某个扇区算错了。验证三闭环低速运行。切换到正常的FOC闭环给定一个较低的速度指令观察q轴电流是否平稳有没有周期性的波动。如果有波动频率正好对应电机的机械旋转频率那大概率是编码器安装偏心或偏移校准不够准。我还会做一个简单的零位检查电机空载以固定速度正转记录几个机械角度和电角度把关系代入θ_e p × θ_m θ_0看看计算出的θ_e是否和控制器内部一致。如果所有值都吻合就说明校准是对的。5. 常见问题与调试实录5.1 极对数算错电机校准后反转或堵转极对数自动获取最典型的失败现象是校准结束后电机在低速时还能转但一超过某个速度就剧烈抖动甚至反转。原因是控制器内部用的极对数和你电机真实极对数不一致导致电子换相速度和机械转速不匹配。排查思路很清晰。先看estimate_pole_pairs的结果是否符合电机铭牌或者绕组的实际情况。如果结果偏小比如7对极电机算出来是6那多半是扫描过程中转子在某几个点失步了。失步通常是因为电流太小、负载摩擦太大、等待稳定时间不够。把I_align调大一点把步长从30°减到15°再把稳定判断阈值从1°收紧到0.3°基本能解决。另外要检查角度累计是否正确。在0°边界附近如果直接用当前角度相减会把359°到1°的变化错算成-358°累计机械角度就会异常。angle_delta这个函数必须处理环绕float angle_delta(float now, float prev) { float d now - prev; if (d 180.0f) d - 360.0f; if (d -180.0f) d 360.0f; return d; }别小看这几行几乎所有极对数计算异常都出在这种看似不起眼的环绕问题上。5.2 偏移校准出来的数值反复跳变很多人会在多点平均后发现偏移值不稳定每次校准相差十几度。这种情况大多数不是算法问题而是编码器安装不好。比如磁钢没有完全对中或者编码器芯片离磁钢太近导致磁场饱和角度读数就会有周期性失真。我遇到过一台电机校准出来的偏移在不同角度点上的离散度达到了±8°。后来拆开一看磁钢偏向了一侧重新贴正后离散度立刻降到±1°以内。所以如果偏移值离散度过大先检查硬件安装而不是一味调软件。还有一个可能原因是电机齿槽转矩太大。在idI_align校准时如果给定电流较小转子可能被齿槽卡在某个位置并没有真正落在电流矢量对准的稳定点上。这种情况的典型表现是每次扫同一个参考角度记录的机械角度都不太一样。解决办法同样是提高校准电流人为增大电磁力矩让齿槽转矩相对可以忽略。5.3 校准完成后带载运行仍然抖动如果校准参数保存了低速空载也正常但带载一重就抖那问题往往出在电流环带宽或者速度环参数上而不是电角度偏移。但有一种情况例外编码器存在明显的非线性误差。这种误差在空载轻载时不明显负载一旦变大转矩波动就会被放大。我常用的办法是把电角度偏移在0°到360°范围内做一个查表校正表把不同机械角度下的偏移微分量补偿进去。这样一来原本因为编码器安装偏心导致的重复性角度误差就能被有效消除。当然这个方法要建立在偏移误差具有重复性的前提下如果误差是随机噪声查表也没用。5.4 实测调参经验最后分享几个我实际总结的参数经验给大家一个参考起点。校准电流I_align建议从额定电流的10%开始根据实际情况上下调整。如果电机很小比如舵机里的空心杯电机0.5A就足够如果是几百瓦的伺服电机可能需要1.5A到2A。原则是保证转子能被稳定拉住又不至于发热过猛。电角度步长15°是一个比较均衡的选择。步长太大可能让转子位置跨过多个电周期步长太小则扫描时间拉长。摩擦力较大的电机建议还用10°保证每一步都能真实反应角度变化。稳定等待时间这个没有固定值我习惯用连续多次读取角度来做判断。连续10次读取每次间隔10ms的最大角度差小于0.5°就认为稳定。如果电机负载惯量很大这个时间可能需要翻倍。有一点值得多说一句自动校准时涉及大电流代码里一定要加超时保护。如果一个步骤超过比如2秒还没等到稳定就说明电流不够或者机械卡死此时应该立刻停止断开电流而不是一直傻等下去。我一开始在这上面吃过亏有一次电机堵转持续大电流差点把MOS管烧了。从那以后我所有校准函数都加上了严格的超时和电流保护逻辑。工具链上我用STM32CubeIDE配合逻辑分析仪抓SPI和PWM波形方便确认AS5047P的数据时序是否正常。调试FOC时建议至少准备一个直流电流探头或者高带宽示波器电流钳光靠板子上的采样电阻观察电流环动态很多时候看不出问题。校准参数做完之后记得把这套流程做成一个可重复调用的函数。电机的批次差异、编码器装配误差都会导致每台电机的偏移略有不同产线上如果能一键校准并自动烧录参数后续维修换件都会省心很多。我自己后来还把这个流程扩展了一步在校准完成后自动执行一个低速往返测试这样连接线问题都能提前暴露出来。这套自动校准方法并不复杂核心就是利用电流矢量拉着转子走再从机械角度和电角度指令的变化关系里反推极对数和偏移。只要把角度环绕、稳定判断、电流大小这三个关键点处理好了几乎所有无刷电机都能在几秒内完成校准。以后换电机再也不用拿万用表倒腾相序和角度了一键搞定。
返回列表