资讯动态

单电阻PMSM无传感FOC全解析:SMO+PLL+电压重建方案

发布时间:2026/10/20 9:33:25 来源:尧图企业网站定制
做电机驱动这段时间被问得最多的一个组合需求就是省掉编码器同时把采样电阻从三个减成一个。这两个需求合在一起就是标题里的这套单电阻PMSM无传感控制方案。我基于TMS320F28035把模型和代码完整跑通过一轮算法路线正好是标题里那四块SMO滑模观测器负责估反电动势单电阻采样负责从母线电流里恢复三相电流PLL负责把反电动势变成平滑的转角和转速电压重建负责把指令电压修正成观测器能信得过的实际电压。这套方案看起来参数不多真正落地要处理的是采样时序、参数标幺、启动切换和一堆边界条件。适合正在做FOC样机、想把编码器省掉的工程师也适合想彻底搞明白单电阻采样和滑模观测器原理的同学。我会把整条信号链拆开讲附上可以往工程里搬的核心代码以及调试中反复遇到的几个典型问题。1. 一套省去编码器和三电阻采样的FOC方案为什么大家都这么搭1.1 成本账与工程账先把方案的动机算清楚先说省成本这件事。一个增量式编码器加上安装结构成本常常占整个驱动板的五分之一甚至更多而且在振动大、油污多、温度高的场合编码器反而是最脆弱的部件。去掉它以后电机侧只剩三根相线系统的可靠性和体积都会改善。代价是位置信号必须从电流和电压里“估”出来这对观测器提出了很高的要求。电流采样侧的减法也一样。三电阻方案需要三个采样电阻、三路差分放大、三路ADC布局时每一路都要考虑共地和噪声隔离。单电阻方案只需要母线负端一个精密采样电阻一路放大一个ADC通道从硬件上说清爽太多。但它把难度转移到了软件上一个采样点不可能同时给出三相电流必须利用PWM开关状态的窗口去“重构”。所以把这两个需求叠在一起就得到了一个典型的低成本无传感FOC硬件形态单电阻采样 无位置传感器。而算法栈之所以是SMO、PLL、电压重建这套组合是因为它们各自解决了一个不可回避的问题SMO从电流和电压里估出反电动势PLL从反电动势里提取出平滑的转子角度和转速电压重建则保证喂给SMO的电压足够接近真实施加在电机上的电压。三者缺一个整条链都不成立。方案成本采样质量算法负担典型应用编码器 三电阻高最好最低伺服、低速高精度无传感 三电阻中好中通用变频器无传感 单电阻低中下需重构高风机水泵、小功率家电单电阻方案的电流质量在低调制区会明显变差这决定了它对控制算法的鲁棒性要求更高后面我会专门说这个坑。1.2 信号链一图看懂谁在给谁喂数据整个系统的信号流可以这样看。FOC主回路还是老一套单电阻重构出三相电流经过Clarke变换得到电流再经过Park变换得到d/q轴电流电流环PI输出交直轴电压逆Park得到两相静止坐标系下的电压指令最后经过SVPWM输出六路PWM驱动逆变器。观测器部分则挂在主回路旁边单独拉一条数据链。SMO需要的输入是两样东西一个是αβ轴下的电流可以从重构后的三相电流直接变换得到另一个是αβ轴下的实际电压这个不能直接拿逆Park输出的电压指令而要由电压重建模块去算。SMO输出估算反电动势PLL再吃下这组反电动势输出转子电角度和电角速度角速度送给速度环角度则送回Park变换和逆Park变换。这里最容易被忽略的就是电压重建。很多人一开始直接用指令电压喂给SMO想着反正SVPWM不是已经输出了吗但指令电压只是“希望”施加的电压实际经过死区、管压降、母线电压波动之后真正落在电机上的电压已经变了。观测器喂进去的电压不准估出来的反电动势自然不准位置就跟着漂。所以电压重建不是锦上添花而是这套无传感方案里绕不开的一环。2. 28035上的单电阻采样在PWM事件里“偷”三相电流2.1 单电阻采样的物理约束三相加起来为零但采样窗口只有一个电机三相电流满足基尔霍夫定律三相瞬时电流之和为零所以理论上只要知道两相第三相就能算出来。问题是单电阻只放在母线负端它上面流过的电流并不是某一相连续的电流而是随着PWM开关状态变化的母线电流。中心对齐PWM下每个开关周期的正中间逆变器通常会有一个有效的电压矢量正在作用此时母线电流就等于某一相电流。具体是哪一相取决于当前电压矢量所在的扇区和开关组合。工程上有一套固定的对应关系有效电压矢量开关组合a b c母线电流等于V1100IaV2110-IcV3010IbV4011-IaV5001IcV6101-Ib可以看到每一个有效矢量对应的母线电流要么是某相电流要么是某相电流的负值。所以只要在PWM周期里找到两个合适的、分属不同有效矢量的采样点就能拿到两相电流第三相用KCL补齐。问题在于这两次采样不是随时都成功的。当电压矢量接近扇区边界或者调制比很低的时候某个有效矢量的作用时间可能只有几百纳秒而ADC从采样保持到转换结束至少需要一段时间窗口不够就采不准。这是单电阻方案最核心的物理约束也是后面很多调试问题的根源。2.2 ePWM与ADC的同步方案采样时刻决定电流质量TMS320F28035上的ADC触发源可以直接挂到ePWM模块的事件上不需要CPU介入这给单电阻采样提供了很大的便利。我把ADC的SOC触发源配置成ePWM计数器的比较事件让ADC在一个载波周期的前半个有效矢量窗口和后半个有效矢量窗口分别触发一次采样。两个采样点都在有效矢量的中间段尽量避开开关切换引起的振铃。配置思路大概是这样// ePWM1负责第一路电流采样触发 EPwm1Regs.TBCTR 0x0000; EPwm1Regs.TBPRD PWM_PERIOD; // 换算成10kHz EPwm1Regs.CMPA.bit.CMPA SAMPLE_POINT_1; // 前半个周期采样点 EPwm1Regs.ETSEL.bit.SOCASEL ET_CTR_CMPA; // 比较匹配触发 EPwm1Regs.ETSEL.bit.SOCAEN 1; EPwm1Regs.ETPS.bit.SOCAPRD ET_1ST; // ePWM2负责第二路电流采样触发比较点设在周期后半段 EPwm2Regs.TBPRD PWM_PERIOD; EPwm2Regs.CMPA.bit.CMPA SAMPLE_POINT_2; EPwm2Regs.ETSEL.bit.SOCASEL ET_CTR_CMPA; EPwm2Regs.ETSEL.bit.SOCAEN 1; EPwm2Regs.ETPS.bit.SOCAPRD ET_1ST;两个采样点之间至少要隔开一个ADC转换周期并且各自对应的有效矢量窗口都要足够宽。如果某一拍的某个窗口太窄就不能再用它采相电流这时候要么对SVPWM做最小脉宽限制要么退回到上一拍的重构结果做保持。我后面会在调试章节详细讲这个处理。2.3 电流重构的最小实现重构逻辑本身不复杂核心是根据当前PWM周期使用的矢量把两次母线采样值翻译成ia和ib再用ia ib ic 0算出ic。typedef struct { float ia; float ib; float ic; } PhaseCurrents; PhaseCurrents Reconstruct_Cuurent(int sector, float sample1, float sample2) { PhaseCurrents cur; switch (sector) { case 1: cur.ia sample1; cur.ib -sample2; break; case 2: cur.ia sample1; cur.ic -sample2; break; case 3: cur.ib sample1; cur.ic -sample2; break; case 4: cur.ia -sample1; cur.ic sample2; break; case 5: cur.ib -sample1; cur.ic sample2; break; case 6: cur.ia -sample1; cur.ib sample2; break; default: break; } cur.ic -cur.ia - cur.ib; return cur; }上面的扇区编号对应经典SVPWM的六个扇区划分每次进中断先根据当前角度和电压矢量方向确定扇区再取两次ADC结果做映射。实际工程里sector和采样点的对应关系必须根据自己PWM模块的配置仔细对表这也是最容易写反的地方。3. SMO滑模观测器的落地方案符号函数、滤波与增益的取舍3.1 观测器模型αβ轴下的两个电流方程滑模观测器建立在αβ轴系的PMSM电压方程上。表贴式PMSM的电压方程可以写成L·dia/dt Va - R·ia - EαL·dib/dt Vb - R·ib - Eβ其中Eα和Eβ就是转子旋转产生的反动势它们的相位直接反映了转子位置。SMO的思路是构造一个电流估计模型把实测电流与估计电流的误差通过开关函数拉回滑模面当估算电流和实测电流一致时开关函数输出的等效值就约等于反电动势。观测器方程写成L·d(ia_hat)/dt Va - R·ia_hat - k·sgn(ia_hat - ia)L·d(ib_hat)/dt Vb - R·ib_hat - k·sgn(ib_hat - ib)当滑动模态建立起来之后估算电流逼近实测电流开关函数中的高频分量经过等效控制平均剩下的低频量就是反电动势Eα和Eβ。这个方法的鲁棒性来自开关控制本身只要增益足够大它对电阻、电感参数的偏差以及电压误差都有很强的“拉回”能力这也是它在工程里比卢恩伯格观测器更好用的原因。3.2 代码实现从电流误差到等效反电动势实际代码里我会用一个结构体保存观测器状态用一阶低通滤波器处理开关函数输出得到平滑的反电动势。typedef struct { float Rs; // 定子电阻Ω float Ls; // 定子电感H float Ts; // 控制周期s float kSMO; // 滑模增益 float alpha; // 低通滤波系数 float ia_hat; float ib_hat; float ea_f; float eb_f; } SMO_Obj; void SMO_Run(SMO_Obj *o, float ia, float ib, float va, float vb, float *ea, float *eb) { float err_ia o-ia_hat - ia; // 估算电流减实测电流 float err_ib o-ib_hat - ib; float za (err_ia 0.0f) ? o-kSMO : -o-kSMO; float zb (err_ib 0.0f) ? o-kSMO : -o-kSMO; // 电流估计方程显式欧拉离散 o-ia_hat o-Ts * (va - o-Rs * o-ia_hat - za) / o-Ls; o-ib_hat o-Ts * (vb - o-Rs * o-ib_hat - zb) / o-Ls; // za/zb的等效值就是反电动势低通滤掉高频抖振 o-ea_f o-alpha * (za - o-ea_f); o-eb_f o-alpha * (zb - o-eb_f); *ea o-ea_f; *eb o-eb_f; }这里有个容易犯错的点符号函数里的误差方向是“估算电流减实测电流”反电动势估计值取za本身。如果反了观测器会发散而不是收敛。我第一次移植时就是因为符号写反电流估计直接飞了后来对着模型推了一遍才发现。3.3 增益与滤波系数怎么定滑模增益kSMO的选择很考验经验。kSMO太小滑模面建立不起来估算电流跟不上实测电流反电动势估计会是一个衰减振荡kSMO太大抖振明显高频分量灌进PLL之后位置噪声变大。我的做法是从0.1×Vbase开始试观察电流估计误差能否在几个毫秒内被“吸”到零能吸进去但噪声偏大就往下调吸不进去就往上加。低通滤波系数alpha决定了反电动势信号的光滑程度。alpha太小反电动势被滤得过于平滑相位滞后严重位置估计会在动态工况下明显滞后alpha太大滤波基本不起作用抖振直接传到角度。一般取alpha对应的截止频率在50Hz到200Hz之间具体要看电机基频范围。基频越高截止频率也要跟着提高否则高速时反电动势相位补偿不过来。低通滤波带来的相位滞后是一个绕不开的问题最简单的补偿办法是查表给估算角度加一个随转速变化的补偿角。更省事的做法是让PLL自己消化这个滞后实际跑下来效果也够用缺点是带宽不能调太高。4. PLL锁相环从反电动势里提取角度参数只有两个坑却有七八个4.1 为什么不直接用atan2抖动、跳变、速度毛刺SMO输出的反电动势本质上是一对随转子位置变化的正弦信号理论上用atan2(Eβ / Eα)就能得到转子角度。但工程上几乎没人直接这么做。原因很简单反电动势信号含有抖振和谐波直接atan2出来的角度会毛刺满天飞而且角度会发生从π到-π的跳变这个跳变一旦进入Park变换电流环输出会瞬间跳一大格。PLL本质上是一个二阶闭环跟踪器它把角度和速度当成一个整体去跟踪而不是逐点求反正切。它有惯性有阻尼对反电动势中的谐波和毛刺有天然的平滑作用同时还能直接输出一个经内部速度积分得到的光滑转速信号。所以SMO后面挂PLL不是多此一举而是让整个估算器从“观测”变成“伺服”。4.2 PLL的误差信号与环路结构PLL的核心是根据反电动势建立误差信号。令Eα和Eβ为反电动势观测值构造误差err (-Eα·cos(θest) - Eβ·sin(θest)) / |E|当θest接近真实转子角度时这个误差近似等于sin(真实角度与估计角度的差)归一化之后幅值不再随转速变化PLL带宽就能在全速范围内保持相对一致。然后误差经过PI调节器去修正速度积分器的输出再累加出角度。参数整定上我习惯把PLL看成一个典型的二阶系统自然频率ωn和阻尼比ζ是关键。比例增益kp近似等于2·ζ·ωn积分增益ki近似等于ωn²。ωn的选择要结合速度环带宽带宽太小动态跟随慢速度突变时角度会“拖后腿”带宽太大系统又容易引入噪声甚至和机械谐振耦合。我一般取速度环带宽的3到5倍同时保证远低于采样频率的十分之一。4.3 PLL代码骨架与正方向陷阱下面是我在28035上用的PLL核心代码代码里kpi和ki按照上面的思路计算具体值需要在台架上微调。typedef struct { float kp; // 比例增益 float ki; // 积分增益 float theta; // 电角度估计rad float omega; // 电角速度估计rad/s float Ts; // 周期s } PLL_Obj; void PLL_Run(PLL_Obj *o, float ea, float eb) { float emag sqrtf(ea * ea eb * eb); float err; if (emag 0.01f) return; // 反电动势太小守住当前状态 err (-ea * cosf(o-theta) - eb * sinf(o-theta)) / emag; o-omega o-ki * err * o-Ts; o-theta (o-omega o-kp * err) * o-Ts; // 角度越界处理 o-theta fmodf(o-theta, 2.0f * 3.14159265f); }这个代码里符号方向是按常用的反电动势符号约定推的但如果你在自己的工程里发现PLL锁到了错误角度不要怀疑硬件先把err那行的符号取反试试。我碰到过两次都是因为SMO输出的反电动势方向定义和PLL的约定不一致导致角度估计和真实位置永远差一个固定角度。检查方法很简单开环运行把估算角度和实际角度同时打出来看到它们同步变化但恒定相差某个值那就是正方向问题。5. 电压重建没有想象中简单死区、零序和采样时刻5.1 指令电压和实际电压之间存在什么很多人以为SVPWM算出来的占空比直接就是施加在电机上的电压实际差得远。第一死区时间里开关管不导通相电压被拉低第二开关管的导通压降和续流二极管压降会造成额外的电压损失第三母线电压不是恒定值整流滤波后的母线会随负载波动第四SVPWM本身为了充分利用母线电压会注入零序分量使三相占空比之和偏离恒定值。SMO的反电动势估计建立在电压方程上如果输入电压比真实电压偏大或偏小反电动势估计就会产生偏差最终表现为位置误差。这类误差在低速时尤其致命因为低速时反电动势本身就小一点电压偏差就能让信噪比恶化。5.2 基于母线电压和实际占空比的三相电压重建所以电压重建不能拿指令值直接算而要拿真正写入PWM寄存器的那个占空比再叠加上实测母线电压。具体实现上我用过下面这个公式它天然考虑了三相占空比的零序分量Va Vbus × (2·da - db - dc) / 3Vb Vbus × (-da 2·db - dc) / 3Vc Vbus × (-da - db 2·dc) / 3原理是先从三相桥臂电压的平均值得到中性点电位再让各相相电压减去中性点电位。这样得到的相电压是星形负载下的真实相电压SVPWM注入的零序分量被自然消掉了。void Voltage_Reconstruct(float vbus, float da, float db, float dc, float *va, float *vb, float *vc) { float vn vbus * (da db dc) / 3.0f; *va vbus * da - vn; *vb vbus * db - vn; *vc vbus * dc - vn; }这三个电压再经过Clarke变换就是SMO需要的Va和Vb。要注意的是da、db、dc必须是当前周期实际装载的PWM比较值对应的占空比而不是电流环输出后、SVPWM运算前的参考值。很多第一次实现的人会拿错变量导致重建电压和实际输出电压之间出现一个周期的延迟这个延迟会让位置估计相位超前或滞后。5.3 死区补偿的最小方案电压重建能修正占空比和母线电压的直接影响但死区和管压降是另一个维度的误差源。最简单的死区补偿方法是根据电流极性给相电压加一个固定的补偿量。typedef struct { float vbus; float vdead; // 死区与管压降折算后的等效补偿电压 } VoltRecon; void Volt_Recon_Comp(VoltRecon *o, float da, float db, float dc, float ia, float ib, float ic, float *va, float *vb, float *vc) { float vn o-vbus * (da db dc) / 3.0f; *va o-vbus * da - vn; *vb o-vbus * db - vn; *vc o-vbus * dc - vn; if (ia 0.02f) *va o-vdead; else if (ia -0.02f) *va - o-vdead; if (ib 0.02f) *vb o-vdead; else if (ib -0.02f) *vb - o-vdead; if (ic 0.02f) *vc o-vdead; else if (ic -0.02f) *vc - o-vdead; }vdead不要只按理论公式算更可靠的办法是在台架上实测。先锁住直流母线给定一个固定占空比用电压探头测实际相电压反推vdead。这个值在电流过零附近会变得很不稳定所以代码里设了0.02A的小阈值避免电流过零时误动作。6. 把估算器接进FOC主循环标幺化、启动序列与中断结构6.1 标幺化换电机不换观测器参数的关键无传感系统最怕的就是换一台电机观测器参数全得重调。解决这个问题的关键是把电学量全部标幺化。我用的基值定义是电压基值为相电压峰值对应的值电流基值为额定电流峰值转速基值为额定电角速度。电阻基值由Vbase/Ibase给出电感基值由Rbase/ωbase给出。标幺化之后SMO里那个增益kSMO的取值范围就变得统一了不再跟着电机功率等级变化。换一台功率大一倍、电压等级相同的电机电机的标幺电阻电感和上一台接近观测器参数基本可以平移过去。这是我在多台样机之间移植代码最省事的一个习惯。参数基值定义单位电压基值 Vbase相电压峰值V电流基值 Ibase额定电流峰值A转速基值 ωbase额定电角速度rad/s电阻基值 RbaseVbase / IbaseΩ电感基值 LbaseRbase / ωbaseH6.2 从开环I/f到无传感闭环的切换序列无传感系统不能启动就切入SMO闭环因为零速时反电动势为零观测器什么都估不出来。工程上的标准做法是用I/f开环强拉启动速度环不工作d/q轴电流由程序直接给定q轴电流维持一个固定幅值角度由程序线性递增按斜坡把转速慢慢拉起来。启动切换的整体序列可以简化成下面的状态机if (state START_IF) { freq_ref freq_ramp * Ts; // 频率斜坡 theta_ref fmodf(theta_ref freq_ref * Ts, 2pi); id_ref 0.0f; iq_ref I_START_AMP; // 反电动势达到阈值PLL角度预置准备切换 if ((freq_ref FREQ_SWITCH_THRESH) (est_emag E_TH)) { pll.theta theta_ref; pll.omega freq_ref; state CLOSED_LOOP; } } else if (state CLOSED_LOOP) { theta_ref pll.theta; // 切换到观测器角度 speed_fb pll.omega; iq_ref speed_pi_output; // 速度环接管 }这里有两个细节特别重要。一是PLL的初始角度一定要预置成当前I/f开环的角度否则切换瞬间角度跳变电流会猛地打一个趔趄。二是iq_ref不要直接从固定启动幅值直接切成速度环输出建议加一个200ms左右的渐变斜坡让电流环平稳过渡。6.3 10kHz中断里的主循环框架整个控制在ADC中断里完成28035处理10kHz的控制频率压力不大。中断服务的顺序决定了数据流动的时间一致性我的安排是先做电流重构再做坐标变换和电流环然后算SVPWM最后跑观测器。interrupt void FOC_ISR(void) { // 1. 从两次ADC结果重构三相电流 PhaseCurrents cur Reconstruct_Sample_And_Update(); // 2. Clarke Park float i_alpha cur.ia; float i_beta (cur.ia 2.0f * cur.ib) * INV_SQRT3; float i_d i_alpha * cosf(theta) i_beta * sinf(theta); float i_q -i_alpha * sinf(theta) i_beta * cosf(theta); // 3. d/q轴电流环PI得到vd、vq // 4. 逆Park得到v_alpha_ref、v_beta_ref送SVPWM // 5. 电压重建用实际占空比和母线电压算观测器电压 // 6. SMO估算反电动势 // 7. PLL更新角度和速度 // 8. 写PWM比较寄存器 }这个顺序里电压重建要放在SVPWM计算之后因为观测器需要的是“真正写到PWM寄存器”的占空比如果用电流环输出值来算就会引入一拍的误差。观测器放最后让SMO吃到的电流采样值、电压重建值和当前PWM输出都是同一拍的数据时间对齐很重要。7. 调试实录哪些坑是我反复踩过的7.1 重构电流在低速时出现锯齿失真第一次跑通无传感闭环时速度一降到5%额定转速以下电流反馈就开始出现周期性锯齿电流环明显高频振荡。当时第一反应是采样电阻的噪声问题加电容、加滤波全试过效果都不明显。后来用示波器同时抓VSENSE波形和PWM输出才发现问题根本不是噪声而是采样点落到了窄脉冲窗口里。低速情况下合成电压矢量的模长很小SVPWM出的有效矢量脉冲宽度不够ADC还没来得及完成采样保持窗口就结束了。这时候采到的母线电流是多个开关状态的混合重构出来的相电流自然就是锯齿状。解决办法分两步。第一步是在SVPWM输出端加最小脉宽限制任何有效矢量的持续时间不得小于1.5μs左右不足的部分按比例调整相邻矢量尽量保持合成矢量的幅值和方向不变。第二步是给ADC采样点做自适应偏移当某个窗口过窄时把采样点往窗口更宽的一侧挪。如果两个窗口都不可用就用上一拍的重构结果暂时代替但要加滞环和限幅不能长时间用历史数据否则电流环会“飞”。7.2 位置估计总带6倍电频率脉动换到中高速稳定运行之后用临时编码器做对照发现位置估计误差里始终含有一个明显的6倍电频率成分转矩电流也跟着周期性波动。这个现象很有规律只要是三相系统出现6倍频分量基本都指向三相不对称或死区效应。排查过程是把电压重建模块的估算电压和示波器实测线电压做对比。一开始没加死区补偿相电压在电流过零附近有明显的凹陷加上5.3节的死区补偿后6倍频脉动幅值降下去一大截。剩下的一小部分来自电流重构误差在扇区切换时的突变通过进一步优化最小脉宽限制和采样点位置后基本被压到可接受范围。这个坑提醒我一个原则不要急着在PLL上加重滤波去压脉动滤波只是掩盖问题。先确认电压重建和电流重构这两个“输入端”是干净的再考虑怎么处理反电动势里的残余谐波。7.3 启动切入闭环瞬间过流切换瞬间过流是我最后才解决的坑。现象是开环I/f强拉阶段一切正常一到切换条件满足、状态机跳到闭环q轴电流瞬间顶到限流值运气不好还会触发硬件过流保护。当时我先怀疑电流环PI输出突变但检查后发现问题出在角度跳变。状态机切闭环时PLL才刚开始从零角度积分而I/f阶段的角度已经走了很远两个角度相差几十度。Park变换突然用这个错误的反馈角度去算id/iq电流环为了追上错误的反馈值输出电压直接冲到饱和。修复办法就是6.2节说的那两步切换前把PLL的theta预置成当前theta_refomega预置成freq_ref切换后iq_ref从启动电流值开始用斜坡在200ms内渐变到速度环输出。这样切换瞬间电流变化率就小很多。再把切换阈值提高一点确保反电动势幅值足够大、PLL有足够的信噪比去锁定。最后再说一个我自己的调试习惯。很多人一上来就把无传感闭环从头到尾跑通才开始看波形这样很难定位问题。我的做法是先开环I/f运行把SMO和PLL当作“观测仪表”跑起来在示波器上同时观测估算角度和真实电流相角确认估算角度与真实位置趋势一致再让PLL输出切入电流环。每一步都在上一个节点验证过了最后无传感启动基本就是一次过。这套方案真正花时间的不是算法本身而是把采样和电压这两个输入伺候干净。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑