
最近整理以前的电机控制笔记翻出一个叫ax的项目文件夹。那时候我接了一个直流无刷电机的驱动调试任务项目代号顺手起了ax结果调试期间天天跟ax、by、cz这三组字母较劲。查手册、翻教材、对着示波器发呆折腾了挺久才搞明白原来这里的ax、by、cz是三相绕组的轴线代号并不是乱码更不是某个神秘算法的缩写。很多人第一次打开 BLDC 的 FOC 框图第一反应都是想搞清楚ax、by、cz到底怎么划分是不是按照垂直轴线来分——我当初也这么想过后来发现这个理解一半对一半不对。这篇文章我就把整套轴系划分的逻辑、坐标变换的流程、工程落地时踩过的坑一次性整理出来给正在学无刷电机控制或者刚开始调 FOC 的朋友做个参考。1. 拆开“ax”这个代号三相绕组的划分逻辑到底是什么1.1 三相绕组不是乱码是空间上的三根轴线电机定子铁芯内壁开了一堆槽槽里嵌着三组线圈分别叫 A 相绕组、B 相绕组、C 相绕组。每一组线圈在空间上都有自己的中心线也就是绕组轴线。工程资料里习惯把 A 相绕组轴线记作xB 相绕组轴线记作yC 相绕组轴线记作z于是就有了ax、by、cz这种写法。简单说ax指的是“A 相绕组的轴线”by是“B 相绕组的轴线”cz是“C 相绕组的轴线”。这三根轴线在空间上怎么排列答案是互差 120 度电角度。注意不是 90 度不是 180 度而是 120 度。为什么非得是 120 度因为三相交流电源的电压、电流波形本来就是互差 120 度电角度绕组也按 120 度对称分布合成磁场才能变成匀速旋转的圆形磁场。打个比方三个人站在圆桌边上往同一个中心方向推转盘发力方向均匀岔开转盘才能平稳转起来如果所有人挤在一个方向用力转盘要么不动要么乱晃。三相绕组就是那三个“人”120 度就是让它们均匀岔开力的位置。1.2 关于“垂直轴线”的误会是怎么来的回到那个问题ax by cz是按照垂直轴线划分的吗这个问题之所以会出现是因为很多人先接触了 FOC 的坐标变换图看到图里有互相垂直的 α 轴、β 轴还有互相垂直的 d 轴、q 轴于是以为电机的三相绕组也是按垂直关系摆放的。这个理解错了一半。错的地方在于ax、by、cz说的是电机里真实存在的三相绕组轴线它们之间是 120 度不是垂直。对的地方在于FOC 算法里确实会构造出垂直的坐标系但那是数学变换的结果不是电机物理结构本身。换句话说“垂直轴线”这个说法来自算法层面而ax by cz属于物理层面。两码事混在一起当然越看越糊涂。这个误会特别典型。我见过不少初学者拿着 FOC 框图问电机里是不是真的有四根轴其实电机定子里只有三组线圈所谓“垂直轴”是站在不同的观察坐标系里看同一组电流矢量。坐标系可以随便选但物理绕组还是那三相谁也没法把它们掰成 90 度。1.3 三个坐标系一次讲清abc、αβ、dq既然聊到坐标系干脆把电机控制里最常见的三套坐标系一次性讲透。第一套是abc三相静止坐标系就是ax、by、cz所在的那套真实坐标系三根轴互差 120 度固定不动绕组电流就是正弦波。第二套是 αβ 两相静止坐标系α 轴和 β 轴互相垂直也是固定不动的可以把三相正弦量映射成两个正交分量。第三套是dq两相旋转坐标系d 轴和 q 轴也互相垂直但是整张坐标系跟着转子磁极一起旋转d 轴对准转子磁极方向q 轴超前 90 度。三套坐标系各有各的用处。abc是物理本体采样回来的是这三相的电流αβ 是中间桥梁方便做矢量运算dq是控制主场因为把交流量变成了直流量PI 调节器才能精确控制。从abc到 αβ 的变换叫 Clarke 变换从 αβ 到dq的变换叫 Park 变换。整个 FOC 的核心说白了就是把三根互差 120 度的轴逐步“掰”成两根垂直的旋转轴然后在旋转坐标系里做控制。坐标系轴间夹角是否旋转对应含义abcax/by/cz120 度否三相绕组物理轴线αβ90 度否两相静止等效轴dq90 度是随转子旋转的磁场轴2. 从120度到90度Clarke和Park到底在做什么2.1 Clarke变换把三根斜轴变成两根垂直轴搞清楚了abc轴的空间关系下一步自然是为什么要费劲把三根 120 度轴换成两根垂直轴直接原因很简单控制算法不喜欢处理三相耦合的正弦量而两相正交量可以拆成独立的标量加减乘除都方便。Clarke 变换就是干这件事的。标准等幅变换的公式是iα (2/3) * (ia - 0.5*ib - 0.5*ic) iβ (2/3) * (√3/2 * ib - √3/2 * ic)如果三相电流满足基尔霍夫定律ia ib ic 0公式可以化简成工程里更常见的写法void clarke(float ia, float ib, float ic, float *i_alpha, float *i_beta) { // 等幅变换系数在后续SVPWM环节统一处理 *i_alpha ia; *i_beta (ia 2.0f * ib) * 0.57735027f; // 1 / sqrt(3) }很多刚入门的朋友看到这种简化写法会懵怎么iα直接就等于ia了因为ic -ia - ib代进标准公式整理之后α 轴分量恰好等于 A 相电流β 轴分量等于(ia 2*ib)/√3。这个推导过程自己拿草稿纸算一遍比看十遍教程都管用。这里还要提醒一个细节Clarke 变换有“等幅”和“等功率”两种系数约定。等幅变换保证变换前后合成矢量的幅值一样系数带2/3等功率变换保证变换前后功率数值一样系数是√(2/3)。两种只是系数不同本质都是把 120 度三轴映射成 90 度两轴。我用的是等幅变换的归一化写法系数不在这一步乘而是留到 SVPWM 里统一折算。新手最容易踩的坑是不同资料混用两套系数导致电流环增益整个偏掉调 PI 的时候总觉得“差口气”。2.2 Park变换把静止垂直轴变成旋转垂直轴Clarke 变换做完电流变成了 αβ 轴上的两个正交分量但这两个分量还是正弦波会随时间来回摆动。PI 控制器最怕这种交流参考量因为纯积分环节跟正弦信号较劲稳态精度永远做不上去。Park 变换的价值就在这里让坐标系跟着转子转起来把 αβ 轴上的交流量变成 dq 轴上的直流量。Park 变换公式如下θ 是转子电角度。id iα * cosθ iβ * sinθ iq -iα * sinθ iβ * cosθ对应代码void park(float i_alpha, float i_beta, float theta, float *i_d, float *i_q) { float c cosf(theta); float s sinf(theta); *i_d i_alpha * c i_beta * s; *i_q -i_alpha * s i_beta * c; }到了这一步id和iq就变成了直流量PI 可以精确跟踪。对永磁同步电机来说id控制励磁磁通iq控制转矩所以我们最终关心的是让id自动收敛到 0、让iq跟住目标转矩电流。这里必须强调Park 变换里的θ是电角度不是机械角度。电角度等于机械角度乘以电机极对数。四极电机极对数是 2同样的机械转角电角度转了两圈。如果不乘极对数θ跟实际转子磁极位置对不上id、iq会出现周期性振荡电机转矩忽大忽小。2.3 坐标变换之后还要“变回去”完整控制链路前面讲的是电流从abc流向dq的过程也就是“采样 - Clarke - Park - PI”。但控制环路到这里还没闭环。PI 算出来的是vd、vq这两个量还活在旋转坐标系里要驱动电机得先把它们逆变换回静止坐标系再交给 SVPWM 生成六路 PWM 波形。逆 Park 变换的公式和代码同样简单很多教程里直接叫 Iparkvoid inv_park(float v_d, float v_q, float theta, float *v_alpha, float *v_beta) { float c cosf(theta); float s sinf(theta); *v_alpha v_d * c - v_q * s; *v_beta v_d * s v_q * c; }为什么要倒腾这一圈因为 SVPWM 是在静止坐标系里计算矢量作用时间的三相逆变器的六个开关管只能在高电平、低电平之间切换没法直接输出“旋转坐标系里的电压”。所以完整的 FOC 链路是abc电流 - Clarke - αβ 电流 - Park -dq电流 - PI -dq电压 - 逆 Park - αβ 电压 - SVPWM -abc三相电压 - 电机。这整条链路里的“轴系”就是在静止和旋转之间来回切换。理解了这条链路再回头看“按照垂直轴线划分”这个问题你会很清楚abc轴是物理的、120 度的αβ 轴和dq轴是数学构造的、垂直的。所谓划分本质就是不同坐标系之间视角的转换。没有哪根轴是“真正的”垂直轴只看你站在哪个参考系里。3. 实操在真实项目里把ax、by、cz用起来3.1 先搞定电流采样和角度反馈理论上限聊得再多最终还是要落到板子上。我在做ax项目时硬件准备阶段就被两个基础环节卡过电流采样和角度反馈。先说电流采样FOC 至少需要两相电流第三相可以通过三相电流和为零算出来。采样电阻方案常见的有双电阻和三电阻高端一点的用磁电流传感器。不管哪种采样时刻必须避开功率管开关切换的瞬间否则会采到振铃尖峰。实际操作上我的做法是把 ADC 触发和 PWM 中心对齐在 PWM 载波周期的谷底附近触发采样这时候下桥导通、电流环路稳定采出来的值最干净。如果是双电阻采样还要注意高低边导通时间不能小于最小采样窗口否则某一路采不到有效电流。上桥占空比太大或太小时都可能出现采样盲区。这些细节不是看手册能看出的都是拿示波器一个点一个点戳出来的。角度反馈上增量式编码器配 Z 信号是最省心的组合霍尔传感器也可以做 FOC但 60 度电角度分辨率对新手不够友好需要额外做角度插值。我建议调试初期直接上编码器哪怕是一个 2500 线的增量编码器也行能把位置反馈的坑隔离开。编码器接线也要注意A、B 相接反会导致速度方向反了电流环变成正反馈电机在启动瞬间就会猛烈抖动甚至飞车。3.2 代码骨架Clarke、Park与逆变换有了采样和角度代码就可以跑起来了。我习惯把坐标变换写成几个独立的函数方便单步调试。Clarke、Park、逆 Park 三段代码上面已经贴过这里把它们合在一起展示一套最小闭环流程// 中断服务函数PWM频率16kHz void current_loop_isr(float ia, float ib, float ic) { float i_alpha, i_beta; float i_d, i_q; float v_d, v_q; float v_alpha, v_beta; // 三相电流 - 两相静止 clarke(ia, ib, ic, i_alpha, i_beta); // 两相静止 - 两相旋转 park(i_alpha, i_beta, theta_elec, i_d, i_q); // PI控制 v_d pi_run(pi_id, id_ref, i_d, dt); v_q pi_run(pi_iq, iq_ref, i_q, dt); // 两相旋转 - 两相静止 inv_park(v_d, v_q, theta_elec, v_alpha, v_beta); // 交给SVPWM生成PWM svpwm(v_alpha, v_beta, ta, tb, tc); }这段代码看起来短但每一行都有讲究。比如theta_elec必须是由编码器值换算来的电角度换算逻辑是theta_elec (encoder_val - offset) * pole_pairs * 2π / encoder_pproffset就是编码器零位对齐时记录下来的偏移量后面专门讲。再比如dt是中断周期16kHz 对应 62.5 微秒。PI 输出限幅、抗积分饱和这些细节我是踩过一轮之后才补上的iq输出限幅要结合母线电压和当前转速不然大油门超调时积分项会越积越深响应反而变慢。3.3 SVPWM输出环节的快速落地SVPWM 是 FOC 里最容易劝退新手的部分但真做起来就那么几步。先用 αβ 电压算扇区再算相邻矢量的作用时间最后安排零矢量插入。扇区判断最常见的写法是把vα、vβ投影到三个互差 120 度的轴线上看信号的组合落在哪个扇区。void svpwm(float v_alpha, float v_beta, float *ta, float *tb, float *tc) { float v1 v_beta; float v2 -0.5f * v_beta 0.866025f * v_alpha; float v3 -0.5f * v_beta - 0.866025f * v_alpha; uint8_t a (v1 0) ? 1 : 0; uint8_t b (v2 0) ? 1 : 0; uint8_t c (v3 0) ? 1 : 0; uint8_t sector a 2 * b 4 * c; // 根据扇区查表得到相邻矢量作用时间 // 这里省略X/Y/Z计算和归一化系数实际项目里需要 // 根据母线电压和PWM周期做完整折算 }很多芯片厂商的电机库直接提供了 SVPWM 的实现比如 ST 的 MCSDK、TI 的 MotorControl SDK里面都有现成的扇区判断和时间计算表。如果你只是想先把系统跑通直接拿官方库是最高效的路子等跑顺了再回头推公式。注意一点SVPWM 的输入电压必须做归一化处理不然同样的 PI 输出在不同母线电压下表现不一致。我习惯把 PI 输出先折算成相对母线电压的标幺值再送进 SVPWM。3.4 编码器零位对齐这步做错一切白搭这一节单独拿出来说因为它太容易出问题而且问题一出就是“电机乱转”“电流飞车”这种大事故。编码器零位对齐的目的是让算法里记录的转子位置和真实转子磁极位置匹配。如果没有对齐你给iq一个力矩指令实际电磁力矩方向可能是歪的甚至反的电机轻则抖动重则烧驱动。我的实用对齐步骤是这样的断开速度环和位置环只留电流环并让id_ref 0、iq_ref给一个很小的固定正力矩。设置theta_elec为某一个固定电角度比如 0让电机开环转到一个“锁死”位置。在锁死位置读取编码器原始值记为offset。把offset写进参数用theta_elec (encoder_val - offset) * pole_pairs * 2π / ppr重新做闭环。之所以会有第 2 步这种操作是因为 FOC 中如果直接给iq力矩而不转角度电机受到力矩后会转到某个自然平衡位置这个位置恰好对应你给定的电角度。每做完一次对齐最好重新上电验证一遍。验证方法很粗暴在低速下给一个固定正方向力矩指令看电机是不是顺着正方向平稳转动如果反转或者卡死多半是offset差了一个电角度周期。我在ax项目上吃过一次亏对齐时用的力矩太小电机还没转到锁定位就被摩擦力卡住了offset错了一大截。后来我把力矩加大到额定值的 40% 左右同时手动拨动转子一个小角度再松开确保它彻底落入齿槽位这个问题才稳定解决。4. 调试现场那些和轴系直接相关的坑4.1 电机抖动不转多半是零位或相序问题三相电机调试时遇到“启动抖动、嗡嗡响、转子来回弹但不转”九成是两类问题零位不对或者相序接反。排查顺序很有讲究我的建议是从简单到复杂别一上来就怀疑控制算法。第一步先用示波器看三相电流波形。如果电流幅值都正常、频率对但电机不转那问题大概率在角度反馈或零位。第二步把电流环改成开环手动给一个递增的电角度斜坡看电机能不能平滑转动。开环能转说明逆变器和电机本体正常开环都不能转重点查 PWM 配置和死区时间。第三步再回到闭环检查theta_elec是不是跟着转子位置真实变化。用手慢慢拨动电机轴看上位机里读到的电角度是否线性变化如果跳变或滞后就是编码器方向或者倍频设置错了。相序接反最容易跟零位错混。判断方法很简单如果offset做了多次对齐都无效交换任意两相电机线再试。有一次我把 A、B 两相线接反然后花了一个晚上调零位最后才醒悟是硬件相序反了折腾的时光全浪费在软件排查上。从此我的调试流程固定下来第一次上电前先用开环转电机验证相序再做零位对齐。4.2 id、iq乱跳先检查采样再检查角度id、iq波形在示波器上看起来像疯狂跳动的毛刺是电机调试里最常见的“病”。碰到这种情况我总结了一条铁律先查采样再查角度最后才怀疑 PI 参数。采样问题通常是 ADC 触发时刻没对准 PWM或者采样电阻上的振铃叠加进了 ADC 结果。判断方法电机不转时看ia、ib的 ADC 原始值如果静置时波形就不是平直的而是有周期性毛刺采样时序一定有问题。还有一种情况是电流ia、ib标幺方向反了导致 Clarke 变换后的 αβ 分量偏离真实值id、iq当然跟着乱。角度问题主要表现为周期性跳变。如果你发现theta_elec在某个位置突然从 359 度跳回 0 度或者每转一圈抖动一次那是编码器极对数和机械角度没匹配好。还有一个隐藏陷阱极对数设置错误不会导致角度跳变但会让id、iq出现二倍频或高频振荡。极对数的正确性可以用反电动势频率来验证电机在某个转速下转起来用示波器测线电压频率除以机械转速就是极对数。4.3 常见故障速查表把调试中遇到过的典型问题整理成表方便以后快速定位故障现象可能原因排查顺序电机上电抖动不转零位偏移、相序接反、编码器线接反开环验证相序 - 零位对齐 - 检查编码器方向启动瞬间反向飞车角度反馈方向与转子实际转向相反交换编码器A/B相或者修改θ符号重新对齐零位id、iq高频毛刺电流采样毛刺、ADC触发时刻偏移检查采样点在PWM谷底降低采样噪声id、iq周期性振荡极对数错、编码器倍频错用反电动势频率验证极对数检查编码器配置转矩忽大忽小PI抗积分饱和不好、iq限幅不匹配增加输出限幅和抗饱和逻辑高速时力矩骤降PWM占空比接近满幅、采样窗口不够检查最小采样窗口必要时调整PWM策略这张表是我自己贴在工作台旁边的。中间有好几次调试调得头大深呼吸回到表格第一行按顺序查一遍基本都能找到问题。调试电机控制本质上就是把一个大系统切成无数个小环节然后一个个做开环验证。哪个环节先验证通过了就把哪里排除掉别抱着“都查一遍”的侥幸心理那是最费时间的。最后分享一点个人体会。我在ax项目里真正想通“轴系”这件事不是看公式看懂的而是自己拿纸笔画了一张从abc到 αβ 再到dq的旋转矢量图然后把电机的真实转动和图上角度日复一日对照。测试时我特别喜欢故意把theta_elec设偏一个角度观察id、iq怎么变一来二去自然就记住了每组公式背后的物理含义。如果你刚开始接触无刷电机控制别害怕ax by cz这种看着像乱码的代号它们背后就是三根实实在在的线圈轴线。把坐标系的空间关系画明白了后面那些变换公式都是水到渠成的事。