搞无传感器控制的人,十有八九都被低速下的转子位置估计折磨过。今天说的这套方案,速度环用自抗扰LADRC,位置和转速估计靠非线性磁链观测器,整套系统在Simulink里搭完模型,还能直接生成嵌入式C代码,非常适合正在做PMSM无感控制、又不想一直被编码器拖累的工程师参考。
这套方案解决的不只是“去掉编码器”这件事,它同时解决两个更实际的问题:一是速度环对负载扰动和模型误差的抑制能力,二是中高速段转子位置观测的稳态精度。传统PI速度环在负载突变时会有明显速降,而LADRC把负载、摩擦、参数变化统一看成“总扰动”,用扩张状态观测器实时估计并补偿掉;非线性磁链观测器则避免了纯反电动势法在低速段信号太弱、难以提取位置的老毛病。如果你正在做风机、泵类、家电压缩机或者工业伺服的无感方案,这篇文章里的模型结构、参数整定和代码生成注意事项,可以直接往你手头的项目上套。
1. 先搞清楚:速度环LADRC加非线性磁链观测器,到底组成了什么架构
1.1 无传感器控制要解决的最核心问题
无传感器控制本质上就是“用算法代替物理传感器”。原来编码器或者旋变告诉控制器“转子现在在哪个角度、转速是多少”,现在这些信息全部要靠电压、电流采样值反推出来。反推这件事,难点不在高速段,而在低速段和动态过程中。
高速时反电动势幅值大,位置信息容易提取;低速时反电动势小到和采样噪声一个量级,再用传统反电动势法就是自找麻烦。非线性磁链观测器走的是另一条路:它不直接从反电动势里找过零点,而是根据电机电压方程构造一个磁链状态观测器,把定子磁链估计出来,再用磁链分量算出转子电角度。磁链本身就是电压积分出来的,不需要等反电动势做大,所以中低速段的可用性比纯反电动势法好很多。
在这套方案里,控制结构还是经典的“电流内环、速度外环”双闭环,只是速度环不再用PI,而是换成了一阶LADRC。电流环依然可以用PI,因为电流环的对象相对明确,电感电阻参数只要别差得太离谱,PI完全应付得来。真正难缠的是速度环外部的负载扰动和内部的观测器估计误差,这些东西交给LADRC处理会更合适。
1.2 速度环为什么用LADRC而不是PI
先看速度环的被控对象。表贴式PMSM在id=0控制下,电磁转矩近似为Te=1.5pψf iq,机械运动方程是:
J dω/dt = Te - TL - Bω
其中J是转动惯量,TL是负载转矩,B是阻尼系数。如果电流环带宽远高于速度环,iq到ω之间就可以简化成一个带未知扰动的一阶模型:
dω/dt = b0·iq_ref + f(t)
这里的f(t)包含了负载转矩、摩擦、电流环跟踪误差、磁链观测器角度误差造成的转矩波动,甚至还包括转动惯量变化。对PI来说,f(t)只能靠积分器慢慢去顶,负载一变,转速先掉下去再爬回来,动态速降很难压住。对LADRC来说,f(t)被当成“总扰动”,由扩张状态观测器ESO估计出来,在控制量里直接前馈补偿掉,所以负载突变时速度跌落更小、恢复更快。
一阶LADRC的控制结构不复杂:
LESO方程: z1_dot = z2 + b0·u + l1·(ω - z1) z2_dot = l2·(ω - z1)
控制器输出: u = (kp·(ω_ref - z1) - z2) / b0
其中z1是转速的估计值,z2是“总扰动”的估计值,kp一般取观测器带宽的某个倍数,l1、l2按带宽法配置。这里最关键的参数是b0,它是对“控制增益”的估计,不需要非常精确,误差部分会被z2当成扰动估计掉,这也是LADRC比PI鲁棒的原因之一。
1.3 非线性磁链观测器在这套系统里的角色
磁链观测器在这个架构里的任务是提供转子电角度和转速。PMSM的定子磁链由两部分组成:一部分是电枢电流产生的定子漏磁链Ls·is,另一部分是转子永磁体产生的磁链ψf。在静止αβ坐标系下,定子磁链可以写成:
ψα = Ls·iα + ψf·cos(θe) ψβ = Ls·iβ + ψf·sin(θe)
所以只要能把定子磁链ψα、ψβ估计出来,再减去Ls·iα、Ls·iβ,就能得到包含转子位置信息的反电动势积分项,也就是“永磁磁链在定子轴上的投影”。转子电角度自然就是:
θe_hat = atan2(ψβ_hat - Ls·iβ, ψα_hat - Ls·iα)
非线性磁链观测器要做的,就是基于电压方程:
dψα/dt = uα - Rs·iα dψβ/dt = uβ - Rs·iβ
构造闭环观测器,加入误差校正项。因为直接对电压积分会有直流漂移和积分饱和问题,所以不能开环积分,必须用观测器里的反馈项把估计磁链“拉”回真实值附近。这个反馈项如果用线性增益,就是普通Luenberger观测器;如果根据误差大小采用非线性函数,比如死区饱和、反正切型增益,就是标题里说的非线性磁链观测器。非线性增益的好处是:误差大时收敛快,误差小时增益压低,不容易放大噪声。
整套系统跑起来之后,数据流是这样的:电流采样做Clarke和Park变换,电流环PI输出dq电压,经过反Park和SVPWM给到逆变器;同时电压和电流送给非线性磁链观测器,观测器输出电角度和转速;转速再送给LADRC速度环,和给定转速做比较,输出iq_ref给电流环。
2. Simulink模型搭建:从电机模型到观测器的完整链路
2.1 电机模型、坐标变换和电流环的快速搭建
搭建Simulink模型时,我习惯从底层电机模型开始,而不是一上来就堆控制算法。先用表贴式PMSM模型,参数放在模型工作区或者一个初始化脚本里,方便后面整定。电流采样模块可以直接用电机模型的输出电流,但真正做工程时要在电流采样通道里加一点延迟和量化噪声,否则模型仿真结果太“干净”,后面生成代码上电就跑飞。
坐标变换模块不建议手写一大堆sin、cos,用FOC库或者手写都行,关键是符号约定必须一致。Clarke变换:
iα = ia iβ = (ia + 2·ib) / √3
Park变换:
id = iα·cosθe + iβ·sinθe iq = -iα·sinθe + iβ·cosθe
这里最容易错的是电角度是转子电角度,不是机械角度。如果你把编码器角度和观测器角度混用,电流环的dq轴会直接偏掉,电流失控是迟早的事。
电流环PI我建议直接用离散PI,积分项要加限幅和抗饱和。电流环带宽一般取1kHz到2kHz,PI参数可以先按典型I型系统整定,再在仿真里微调。电流环输出的是vd、vq,经过反Park变换得到vα、vβ,然后进SVPWM。
2.2 SVPWM与逆变器模型的处理方式
做控制系统验证的时候,SVPWM可以用平均值模型,也就是直接把vα、vβ除以直流母线电压得到占空比。这样仿真速度快,也不会出现PWM载波带来的高频纹波。但如果你想验证代码生成和真实逆变器行为,建议用带三角载波的SVPWM模块,PWM频率取10kHz左右,死区时间按实际硬件设置。
平均值模型和带载波模型在观测器验证上的差别很大。平均值模型下,电压指令和实际施加电压几乎一致,观测器会表现得非常理想;带载波模型下,实际电压含有PWM谐波,电流采样也有纹波,观测器增益稍微给大一点就会抖。所以我的建议是:先把平均值模型跑通,确认控制逻辑没问题,再切换成带载波的PWM模型去验证观测器的鲁棒性。这一步能省掉大量后面在硬件上排查噪声问题的时间。
2.3 LADRC速度环的Simulink实现
LADRC速度环在Simulink里可以用MATLAB Function模块实现,也可以拆成纯Simulink模块搭。用MATLAB Function模块最直观,代码写起来也接近后面要生成的C代码。下面是一个离散一阶LADRC的实现示例:
function [u, z1_next, z2_next] = ladrc_speed(w_ref, w, z1, z2, b0, kp, l1, l2, Ts) % 一阶LADRC速度环,离散欧拉法 e = w_ref - z1; u = (kp * e - z2) / b0; % 输出限幅,iq_ref上限 u_limit = 10; if u > u_limit u = u_limit; elseif u < -u_limit u = -u_limit; end % LESO状态更新 z1_next = z1 + Ts * (z2 + b0 * u + l1 * (w - z1)); z2_next = z2 + Ts * (l2 * (w - z1)); end这里有个容易被忽略的点:控制量u进入LESO时,要用限幅之后的值,不能用限幅前的值。否则控制器已经饱和了,观测器还在按未饱和的控制量估计,总扰动估计会出现偏差,退出饱和那一下很容易超调甚至震荡。
另外,速度环的采样周期不需要和电流环一样快。PMSM速度环一般1ms执行一次就足够了,电流环还是0.1ms。LESO虽然是在速度环周期里更新的,但如果你把LESO放在电流环周期里跑,状态更新更平滑,代价是MCU计算量变大。工程上我一般放在速度环周期,配合前馈补偿,效果完全够用。
2.4 非线性磁链观测器与角度转速提取
非线性磁链观测器的Simulink搭建可以分成三块:磁链状态更新、电流磁链补偿、角度和转速提取。
磁链状态更新部分,按照下面这个离散形式搭:
ψα_hat(k+1) = ψα_hat(k) + Ts·(uα - Rs·iα + ω_hat·ψβ_hat(k) + g1·(ψα - ψα_hat(k))) ψβ_hat(k+1) = ψβ_hat(k) + Ts·(uβ - Rs·iβ - ω_hat·ψα_hat(k) + g1·(ψβ - ψβ_hat(k)))
这里的“真实磁链”ψα、ψβ在仿真里可以直接用电机的内部磁链信号,在硬件上当然拿不到,所以实际观测器里的误差项要用其他方式构造。常用做法是用电流模型和电压模型交叉校正,或者用反电动势误差做校正。为了仿真验证方便,可以先在模型里把电机的磁链信号引出来做对比,等算法逻辑成熟了,再把误差项改成可实现的电流误差形式。
电流磁链补偿部分很简单:
θe_hat = atan2(ψβ_hat - Ls·iβ, ψα_hat - Ls·iα)
但要注意atan2的结果范围是-pi到pi,角度在跨pi边界时会跳变。如果直接把跳变的电角度差分算转速,会出现一个巨大的尖峰,转速估计直接爆炸。所以后面一定要接一个角度解算模块,做角度连续化,或者干脆用电角度过PLL来提取转速,不用差分法。
转速提取我推荐用PLL,而不是简单差分。PLL本质上是一个闭环,把角度误差变成转速校正量,本身带有滤波特性,对角度噪声不敏感。Simulink里可以用一个PI控制器加积分器搭二阶PLL,输入是θe_hat,输出是转速。PLL带宽不要取太高,20Hz到50Hz足够,太高会把角度噪声放大到转速里。
3. 参数整定:那些增益和带宽到底怎么给
3.1 LADRC的b0、kp、l1、l2整定方法
一阶LADRC需要整定的参数就四个:b0、kp、l1、l2。看着简单,实际坑不少。
先算b0。如果运动方程是J dω/dt = Te - TL,且Te=1.5pψf iq_ref,那么理想b0=1.5pψf/J。这个值用电机铭牌参数算一下就行。问题是J往往不准,而且负载转动惯量还会变。我的习惯是先用理想值做基准,然后在仿真里把b0往小调一点,调到0.7到0.9倍左右,因为b0偏小会让观测器高估扰动,控制器会稍微保守一点;b0偏大则容易引起高频抖动。
kp可以按控制器带宽ωc来定,kp=ωc。速度环带宽不用太高,20rad/s到60rad/s已经能满足大多数风机、泵类负载。你如果把kp直接拉到100以上,速度响应是快了,但电流环会被速度环逼到饱和,最后表现为启动时“窜一下”,加载时“抖一阵”。
l1、l2按观测器带宽ωo配置,一阶系统的LESO增益取:
l1 = 2·ωo l2 = ωo^2
ωo一般取3到7倍的ωc,太小则扰动估计滞后,太大则噪声放大。我调试时习惯先固定ωc=40,然后从ωo=3ωc开始往上加,每次乘以1.2,直到速度波形出现高频毛刺,再退回上一个值。这个方法不高级,但很实用。
给一张我常用的初始参数表:
| 参数 | 含义 | 推荐范围 |
|---|---|---|
| b0 | 控制增益估计 | 0.7~1.0倍理想值 |
| kp | 控制器比例增益 | 20~80 |
| l1 | LESO第一增益 | 2ωo |
| l2 | LESO第二增益 | ωo² |
| ωc | 控制器带宽 | 20~60 rad/s |
| ωo | 观测器带宽 | 3~7倍ωc |
3.2 非线性磁链观测器的增益怎么选
磁链观测器里那个g1,本质上决定观测器把估计磁链拉回真实磁链的速度。g1太小,积分漂移压不住;g1太大,高频噪声会被灌进观测器,电角度会抖。
一个比较稳的做法是设一个观测器带宽ω_flux,g1=2ω_flux。ω_flux可以取100到500rad/s,具体看电机基频和采样频率。仿真时可以先从200rad/s开始,看磁链估计波形是否平滑,再逐步调整。
但真正让“非线性”发挥作用的,是误差项的处理方式。如果直接用线性误差项,g1一旦取大,噪声就跟着大;取小,动态跟不上。非线性增益可以在误差大的时候用高增益,误差小的时候自动降增益。Simulink里最简单的实现就是用饱和模块配合查表,或者写一个MATLAB Function做分段增益。
我实际调试的经验是:非线性增益不要搞太复杂,一个分段线性就够了。比如误差绝对值大于某阈值时增益为g1_high,小于阈值时为g1_low,中间平滑过渡。这样既保留了非线性观测器的优点,又不至于因为增益函数太怪导致代码生成后行为不一致。
3.3 一组可以直接跑起来的仿真参数
下面这组参数是我在某个模拟项目X里用过的,表贴式PMSM,额定转速3000rpm,额定电流不大,适合在Simulink里做验证:
| 项目 | 数值 |
|---|---|
| 直流母线电压 | 310V |
| 定子电阻Rs | 2.875Ω |
| 定子电感Ls | 8.5mH |
| 永磁磁链ψf | 0.175Wb |
| 极对数p | 4 |
| 转动惯量J | 0.001kg·m² |
| 电流环周期 | 0.1ms |
| 速度环周期 | 1ms |
| PWM频率 | 10kHz |
| LADRC ωc | 40rad/s |
| LADRC ωo | 200rad/s |
| 磁链观测器带宽 | 300rad/s |
用这组参数跑空载启动,给定转速1000rpm,速度环基本能在0.1s内跟上,稳态误差接近零。加载后速度跌落大约能控制在给定转速的2%以内,恢复时间取决于负载突变幅度。
4. 从Simulink模型到可生成的嵌入式C代码
4.1 建模型时就要为代码生成做的准备
标题里说了“可生成代码”,这几个字看起来简单,但真要把模型变成能在单片机里跑的C代码,建模规范必须从一开始就卡死。
第一,求解器必须选固定步长离散,不能选连续求解器。连续积分模块在代码生成时要么被替代,要么直接报错。速度环和观测器里所有积分都用离散形式,也就是Unit Delay加反馈,或者直接在MATLAB Function里写状态更新。
第二,不能有代数环。特别是角度提取那一块,如果atan2后面再接一个反馈,很容易形成代数环,仿真能跑但代码生成会报错。解决办法是给反馈回路加一个Unit Delay,哪怕只延迟一个采样周期,工程上完全可接受。
第三,所有模块的输出类型必须明确。不要用继承类型到处乱传,生成代码前把所有信号都显式设置成single或者double。如果你目标MCU不支持浮点或者浮点太慢,还得提前规划好定标。
第四,Scope、Display这类可视化模块在生成代码前要删掉或放在单独的子模型里。代码生成时会把信号记录模块处理成日志代码,虽然不影响功能,但会白白增加代码体积和RAM占用。
4.2 LESO、磁链观测器离散形式的代码生成友好写法
前面LADRC那个MATLAB Function已经是很友好的写法了,输入输出都是标量,状态用持久化变量。要注意的是,持久化状态变量在模型初始化时最好赋初值,否则第一次执行结果不确定。
磁链观测器如果也写在MATLAB Function里,最好把磁链和转速估计都作为函数内部持久化状态,或者通过外部Unit Delay反馈回传。我倾向于用前者,代码更紧凑,也不容易因为信号线连错导致代数环。核心代码大致长这样:
function [theta_hat, speed_hat, psi_alpha, psi_beta] = flux_observer(u_alpha, u_beta, i_alpha, i_beta, Rs, Ls, g1, g2, Ts) persistent psi_a psi_b w_hat; if isempty(psi_a) psi_a = 0; psi_b = 0.175; % 初始磁链给到d轴附近 w_hat = 0; end % 定子磁链观测器 psi_a_next = psi_a + Ts * (u_alpha - Rs*i_alpha + w_hat*psi_b + g1*(psi_a - psi_a)); psi_b_next = psi_b + Ts * (u_beta - Rs*i_beta - w_hat*psi_a + g1*(psi_b - psi_b)); % 转速自适应律 w_hat_next = w_hat + Ts * g2 * (psi_a*(psi_b - psi_b) - psi_b*(psi_a - psi_a)); psi_a = psi_a_next; psi_b = psi_b_next; w_hat = w_hat_next; % 永磁磁链角度 theta_hat = atan2(psi_b - Ls*i_beta, psi_a - Ls*i_alpha); speed_hat = w_hat; end上面的代码是个简化的框架,里面的误差项写成了符号形式,自己实际用的时候要换成可实现的误差表达式。重点是让状态更新严格遵守采样周期,不能在一个函数里连续跑两遍。代码生成之后,这个函数会被映射成C函数,每个PWM中断周期调用一次。
4.3 生成代码之后的离线验证和硬件注意点
代码生成不是终点,生成完还必须做“仿真结果 vs 代码行为”的一致性检查。最省事的办法是处理器在环测试,也就是把生成的代码放到目标MCU里跑,Simulink继续跑电机模型,通过串口把电流电压喂给代码,再把代码输出的PWM占空比读回来。
这里最容易出现的问题是浮点运算差异。Simulink仿真默认双精度,而很多MCU的浮点单元只支持单精度。你模型里所有double到了代码生成后会被替换成float,观测器和LADRC的积分精度随之下降,低速时磁链估计可能会出现明显漂移。解决方法是建模型时就主动把信号改成single类型,用单精度从仿真开始就验证一遍。
另一个坑是代码生成的除法。MCU上浮点除法很贵,而且不同编译器优化结果不同。LADRC和观测器里的除法如果能换算成乘法,比如把1/b0预先算好存储,就能减少不少计算量。Simulink里可以用增益模块代替除法,代码生成后更清爽。
5. 调试实录和避坑清单
5.1 现象:电机低速启动失败,或者一转就抖
这个现象十有八九出在初始位置不准确上。磁链观测器是从零状态开始收敛的,如果初始磁链给得和实际转子位置差太远,电机一开始的电角度就是错的,电流环一开始就可能把转矩方向搞反。解决办法有两条:一条是启动前先做一个预定位,给一个固定的d轴电流把转子拉到已知位置;另一条是用I/F或者开环强拉启动,等转速过了某个阈值再切到观测器闭环。
千万不要指望磁链观测器在零速附近自己“神奇”地收敛。它需要反电动势信息,而零速下反电动势根本没有,这是物理决定的。零速带载启动必须配合初始位置辨识或者高频注入。
5.2 现象:观测器发散,角度跳变,转速乱飞
先看Rs参数准不准。磁链观测器对Rs非常敏感,因为电压方程里Rs·i这一项在低速时和反电动势同量级,Rs误差大一点,磁链估计就像积分漂移一样偏出去。很多调试现场温度一变,Rs变个20%,观测器就开始不正常。
再查角度跳变。atan2输出跨pi边界时,如果后续PLL处理不当,转速会突然出现尖峰。解决方法是把角度误差做卷绕处理,确保误差始终在-pi到pi之间,PLL输入端不要直接把两个周期角度相减,而是把误差写成sin(θ_ref - θ_hat)这种形式。
还有一个很容易忽略的点:电压uα、uβ到底用的是指令电压还是实际相电压。用指令电压省事,但指令电压有死区误差和管压降误差,低速时影响很大。上硬件后如果发现低速磁链漂移,可以考虑加死区补偿,或者用母线电压和开关状态重构实际电压。
5.3 现象:LADRC响应很快,但整个系统一直在抖
大概率是ωo取得太高。观测器带宽高于系统噪声频段以后,总扰动估计里会混入大量噪声,再经过去控制量前馈,相当于给速度环注入高频扰动。我遇到过有人把ωo拉到1000rad/s,速度环看着响应极快,但电流波形全是毛刺,电机噪声大得离谱。
还有一个常见原因是b0给得太大。b0偏大时,LADRC控制器会认为“控制量很有效”,给出的iq指令就会偏小,但ESO为了补偿误差又会把总扰动估计得很大,最后整个环路的增益都失控。调LADRC和调PI不一样,不能只盯着响应速度,要把电流指令波形一起看。iq_ref有没有高频抖、有没有频繁进出饱和,才是判断参数是否合理的核心指标。
5.4 现象:生成代码后跑起来,和Simulink仿真完全两个样
这个现象我见得太多,原因九成是模型和代码之间的“隐性差异”。比如Simulink里用连续积分器,代码生成后变成了离散积分器;比如模型中某个饱和模块在代码生成时被优化掉了;比如数据类型的溢出处理不一致。
排查方法是分层验证:第一阶段只用纯仿真模型,确认控制逻辑;第二阶段把所有采样时间改成离散、类型改成single,再跑一遍仿真;第三阶段把输出限制、饱和、溢出处理全部显式写出来,不做隐式依赖。这三个阶段如果能保持一致,生成代码后基本不会出大问题。
6. 最后再讲两句真话
这套“速度环LADRC加非线性磁链观测器”的架构,我用下来的体会是性价比非常高。LADRC的代码量比PI大不了多少,但对负载扰动的抑制能力肉眼可见;非线性磁链观测器虽然调参比反电动势法费点劲,可一旦把Rs辨识和角度PLL处理好,中高速段的稳定性明显更好。
我踩过几次坑之后的最大感触是:这种高性能控制方案,真正决定成败的不是那些花哨的算法公式,而是离散化、限幅、饱和、数据类型、角度连续性这些“脏活累活”。做仿真时别急着拉高带宽,先把每一步信号的数据流捋清楚,把每个模块在代码生成后可能出现的差异想明白,后面上硬件才会顺。
如果你正准备做自己的无传感器控制平台,我建议先用这组架构搭一个最简单的版本,电流环用PI、速度环用LADRC、观测器用线性增益先跑通,然后再把线性增益改成非线性。一步一步来,比直接照搬论文里的完整算法要稳得多。最后再分享一个小技巧:把观测器带宽、LADRC的ωo、ωc都做成模型里的可调参数,用Simulink的调参功能在线整定,你会发现自己调试效率能翻一倍。