☰
基于LADRC与非线性磁链观测器的PMSM无传感器控制实现
2026/10/19 10:59:46 网站建设 项目流程

最近把一个伺服的编码器拆了,速度环从经典PI换成了自抗扰LADRC,角度反馈交给非线性磁链观测器去估,整个控制链路在Simulink里搭了一遍,最后通过Embedded Coder直接生成了C代码。做PMSM无传感器控制的人应该一眼就能看出来,这条路线的核心是把"位置传感器"和"速度PI"两块最容易被盯上的地方都换成了软件方案。文章后面会把这套系统的原理、建模、参数整定、代码生成和排坑过程完整梳理一遍,适合正在写相关课题、准备把仿真算法往样机上移植的人参考。

先交代一下整体结论:电流环仍然用dq轴双PI,速度环用一阶LADRC,转子位置和速度由非线性磁链观测器加PLL提供,低速段用I/F强制换相起动,到了反电动势能撑起观测器的转速再切换到无感闭环。整套方案在10kHz电流环、1kHz速度环的采样结构下跑得比较稳,代码生成后也能平移到主流浮点MCU上。下面按我实际做的顺序,从"为什么这么搭"一直讲到"实测到底会踩哪些坑"。

1. 这套组合解决什么,以及为什么这么搭

1.1 拆掉传感器之后,系统少了什么又多出什么

矢量控制的根本问题是要知道转子磁场在哪。dq坐标系下的电流解耦、转矩输出全都依赖转子角度,所以传统方案一定会装编码器或旋变。但在很多实际场景里,这个部件恰恰是最想省掉的:压缩机、油泵、风机这一类环境,振动大、温度高、轴端空间又紧,编码器线缆和接插件往往是故障率最高的环节;从成本角度看,一个绝对值编码器加解码电路,对整机BOM的贡献不小。

去掉传感器之后,你不再直接拿到角度和速度,而是需要一个"软件旋变"来估计它们。这里有个基本规律:越接近零速,反电动势越小,基于反电动势和磁链信息的观测器越难工作。所以业界通常把无感运行分成两段——零速和极低速用高频注入或I/F强制换相这类开环/半开环手段,中等转速以上才放心交给磁链观测器。我这套方案选的就是I/F起动加非线性磁链观测器,回路少、实现直接,工程上最省事。

1.2 LADRC和非线性磁链观测器在环路里的分工

控制结构上,内环还是经典的dq电流PI,负责把电感、电阻造成的电流耦合压下去;外环是LADRC速度环,替代原来的速度PI;中间夹着的就是非线性磁链观测器,它给出转子角度,再由PLL给出速度。

这里要解释一个容易被忽略的点:为什么不继续用速度PI?无感速度信号本身就带着相位滞后和噪声,PI要靠积分器慢慢去消负载扰动,固定增益很难同时照顾"动态响应快"和"稳态不抖"。LADRC的思路是把负载、摩擦、参数变化全部装进一个"总扰动",用扩张状态观测器实时估出来再补偿掉。放在无感系统里还有一个附带好处:观测器输出z1本身就是对速度信号的一阶滤波,相当于把速度噪声也顺手处理了一部分。

磁链观测器的角色则是提供θe和ωe。和直接估反电动势的滑模观测器相比,电压模型加矫正项的磁链观测器不用在输出端串低通滤波器,角度提取更平滑,相位滞后更好控制。下面两章我就把这两块的核心原理拆开讲。

2. 速度环LADRC:建模、离散实现与参数整定

2.1 机械方程怎么化成"积分器+总扰动"

PMSM机械运动方程写成:

J dωm/dt = Te − TL − Bωm

其中Te = 1.5 p ψf iq,J是转动惯量,B是阻尼系数,TL是负载转矩。把Te代进去,等式两边除以J:

dωm/dt = (1.5 p ψf / J) iq + (−TL − Bωm)/J

记b0 = 1.5pψf/J,它代表控制输入iq对转速变化率的名义增益。那么模型就可以写成:

dωm/dt = b0·iq + d(t)

这里的d(t)就是"总扰动",它不仅包含负载和摩擦,还包括电机参数漂移造成的模型误差,比如实际ψf、J和标称值不一样时,多出来的那部分也会折算进d(t)。这种做法的妙处在于:不管扰动内部多复杂,只要它可微且有界,扩张状态观测器就可以把它当成一个额外的状态去估计。

2.2 LESO与控制律的离散迭代式

状态取x1=ωm,x2=d,系统扩维成:

x1_dot = x2 + b0·u x2_dot = h(t)

h(t)是扰动变化率,被看作未知但有界量。对这个二阶系统设计线性扩张状态观测器,最常用的极点配置是让两个观测极点都落在−ωo处:

beta1 = 2·ωo beta2 = ωo²

LESO方程在连续域里写是:

z1_dot = z2 + b0·u + beta1·(ωm_meas − z1) z2_dot = beta2·(ωm_meas − z1)

控制律取:

u0 = kp·(ωref − z1) u = (u0 − z2)/b0

z1是转速估计,z2是总扰动估计,kp就是等效闭环带宽ωc。扰动被补偿之后,被控对象从"积分器+扰动"退化成一个纯积分器,再配上P控制器就是典型一阶系统,闭环带宽约等于kp。

在1kHz速度环里,我直接用前向欧拉离散,写成迭代式:

% 速度环LADRC function iqRef = ladrc_speed(wRef, wMech, Ts, kp, wo, b0) persistent z1 z2 uPrev if isempty(z1) z1 = wMech; % 初始化为当前转速,避免启动瞬间冲击 z2 = 0; uPrev = 0; end beta1 = 2*wo; beta2 = wo*wo; e = wMech - z1; % 注意这里u用的是上一拍的值,避免形成代数环 z1 = z1 + Ts * (z2 + b0*uPrev + beta1*e); z2 = z2 + Ts * (beta2*e); u0 = kp * (wRef - z1); u = (u0 - z2) / b0; uPrev = u; iqRef = u; end

有一点必须提醒:ESO用的u应该是"实际进入被控对象的iq",而不是"没经过限幅的iq_ref"。如果电流环限幅之后iq只能给到5A,而LADRC输出的是7A,ESO按7A去估计扰动,就会以为电机被使劲驱动了但转速没跟上,从而把扰动越估越大,这就是无感系统里典型的"扩张状态观测器积分饱和"。我在代码里直接把限幅后的iq送回来参与ESO更新。

2.3 参数初值和调参方向速查

手动试一组经验初值比从零开始乱给要快得多。我用的一台表贴式电机参数是:Rs=1Ω,Ls=5mH,ψf=0.1Wb,极对数p=4,转动惯量J=0.0005kg·m²,额定电流5A。算下来b0_ideal = 1.5×4×0.1/0.0005 = 1200 rad/s²/A。

速度环采样Ts=1ms,初始参数可以按下面这个表给:

参数初值建议说明
kp(ωc)20~60 rad/s相当于传统PI等效带宽,先给30
ωo3~5×ωc取150左右,再根据噪声情况调
b00.7~1×b0_ideal不需要精确辨识,容错范围大

实际冒调的时候,方向记着这几点:响应慢先加kp;负载扰动后恢复慢先加ωo;转速噪声明显就减ωo;b0偏大会让控制量偏小、整体迟钝,b0偏小会让补偿动作过猛,容易在速度上看到高频抖动。还有个规律可以共享:速度环采样率只有1kHz时,ωo不建议超过300rad/s,不然离散误差和速度噪声会把ESO喂出毛刺。

2.4 为什么这比PI更适合无感速度环

做个直观对比:

对比项速度PI速度LADRC
负载突变响应靠积分缓慢消除稳态误差,动态跌落大ESO实时估计负载,直接前馈补偿
对模型的依赖依赖Kp/Ki和J、ψf的匹配b0大致准确即可,误差折进扰动
抗速度测量噪声低增益时较稳需要控制ωo,自带滤波特性
参数含义Kp和Ki互相牵连kp决定带宽,ωo决定观测速度,b0决定增益,互不纠缠

无感系统里的速度反馈来自PLL,本身就带延迟,PI参数稍微激进一点就容易振荡。LADRC把"补偿负载"这件事从积分器里拆出来交给ESO,速度环动态被显著改善。实测中我给同一个电机加阶跃负载,PI方案转速跌落接近60rpm、恢复时间约150ms,LADRC方案跌落大概30rpm、80ms内就稳回来。数字当然随电机走,但趋势是稳定的。

3. 非线性磁链观测器:从电压模型到角度提取

3.1 αβ坐标系下磁链与电压电流的关系

把PMSM写在静止αβ坐标系里,定子电压方程是:

u_α = Rs·i_α + dψ_sα/dt u_β = Rs·i_β + dψ_sβ/dt

定子磁链由两项组成:电枢电感产生的磁链和转子永磁体产生的磁链:

ψ_sα = Ls·i_α + ψf·cosθe ψ_sβ = Ls·i_β + ψf·sinθe

如果能把ψ_sα和ψ_sβ估出来,再减掉Ls·i,剩下的就是永磁体磁链矢量,它的角度就是转子电角度θe。这个思路本身不难,难点在工程实现上,尤其是电压模型的积分环节。

3.2 纯积分器的两个致命问题

最直接的做法是把电压方程两边积分:

ψ_sα = ∫(u_α − Rs·i_α)dt ψ_sβ = ∫(u_β − Rs·i_β)dt

这个纯积分器在仿真里看起来很美,一放到实物上就完蛋,原因有两个。

第一是直流偏置。ADC采样偏置、逆变器死区、指令电压重构误差,都会在u−Ri里混入微小直流分量。积分器对直流是"无限增益",几十毫伏的偏置积分几秒钟就能变成几十毫韦伯的假磁链,角度直接飞掉。第二是初始值。积分需要初始条件,如果启动瞬间给定的磁链初始值和实际差一大截,这个差相当于恒定的直流误差,会一直以角度偏移的形式存在。

有人会想到用高通滤波器去滤直流,但高通滤波器本质上是个一阶惯性环节,截止频率附近会同时压制幅值、拉偏相位。低速运行时反电动势频率本来就低,高通一插进去,角度误差大到没法用。所以必须用带矫正项的观测器,而不是裸积分。

3.3 带非线性矫正项的磁链观测器

我用的观测器写成这个形式:

dψ̂_sαβ/dt = u_αβ − R̂s·i_αβ + k1·Δi_αβ + k2·φ(Δi_αβ)

其中电流误差由"磁链反算电流"和实测电流的差得到:

î_αβ = (ψ̂_sαβ − ψf·[cosθ̂; sinθ̂]) / L̂s Δi_αβ = i_αβ − î_αβ

φ(Δi)是饱和或带死区的非线性函数,我常用sat(Δi/ε0)。这里面的逻辑是:电压模型负责驱动磁链朝真实方向走,电流误差项把积分轨迹往"电流模型磁链圆"上拉;线性项k1提供常规收敛,非线性项k2在误差大的时候用力拉、稳态时保证鲁棒性,还能抑制积分漂移。纯符号函数会带来抖振,工程上基本不用。

观测器增益我给一组可复用的起始值:k1≈2ωobs·L̂s,ωobs取200~500rad/s;k2取0.5~1V;ε0取0.1~0.15A。Rs的标定很重要,定子电阻随温升变化百分之几十是常事,Rs不准在低速时会让磁链角产生明显偏差,有条件就做在线辨识,没条件至少做离线温度补偿。

3.4 角度和速度的提取方式:PLL与延时补偿

从磁链观测器得到永磁体磁链分量之后:

ψ̂mα = ψ̂sα − L̂s·i_α ψ̂mβ = ψ̂sβ − L̂s·i_β

角度提取我不用atan2直接算,而是用PLL跟踪。atan2得出来的角度每拍都在跳,直接微分速度全是噪声;PLL本身对磁链矢量做低通跟踪,角度和速度同时出来,抗噪能力强得多。PLL误差写成:

e_pll = ψ̂mβ·cosθ̂ − ψ̂mα·sinθ̂ ≈ ψf·sin(θe − θ̂)

误差送入PI,PI输出就是电角速度ω̂e,再积分得到θ̂。这个误差符号在搭模型时非常容易搞反,第一次上电如果发现PLL发散,先把PLL单独开环给一个固定角度跑起来,确认反馈极性再接闭环。

还必须提延时补偿。电流环从ADC采样到SVPWM真正输出,通常有1~1.5个PWM周期的滞后。在10kHz载频下,电机电频率一旦到200Hz,1.5个周期的角度滞后就是10°以上,这个量级已经足以让电流环解耦恶化。所以输出给坐标变换的角度要加补偿:

θ_comp = θ̂ + T_delay·ω̂e

T_delay按实际中断时序取1.5×Ts_pwm左右,仿真或实物都可以先用这个值起步。

4. Simulink全链路仿真:结构与起动策略

4.1 顶层模型结构与采样速率划分

整个模型我分成"电机与功率部分"和"控制器部分"两大块。电机用Simulink里现成的PMSM电气模型,逆变器先用平均值模型验证控制算法,再换成带SVPWM的开关模型看波形细节。控制器部分从顶层往下分:速度环LADRC、dq电流PI、αβ磁链观测器加PLL、坐标变换和SVPWM。

采样速率这样分配:PWM与电流环10kHz,所有电流环控制、磁链观测器、PLL都跑在这个节奏上;速度环1kHz,从PLL拿到的速度信号通过Rate Transition块降采样进入LADRC。不用把所有模块都塞到同一个速率里,那样又慢又不符合实际芯片的中断结构。

4.2 LADRC与观测器在Simulink里的离散实现

对于LADRC,我直接用一个MATLAB Function块,代码就是2.2节那段。观测器也可以用MATLAB Function,但要注意两点:所有变量必须是定长数组,不能出现动态分配;状态用persistent变量保存,在块首次调用时初始化。观测器内部的电压不是直接用给定值,而是用母线电压和当前占空比重构出来的"实际电压",仿真里如果做SVPWM,这一点尤其重要,否则死区影响全部被忽略。

PLL也写成离散形式,每个PWM周期更新一次。这样角度环带宽可以做高一点,尽量减小因为角度更新慢带来的额外相位滞后。

4.3 零速起动:I/F开环切换策略

磁链观测器在零速时反电动势为零,本质上"看不见"转子位置,所以必须经历一个开环起动阶段。我用的是I/F强制换相:给定一个固定的电流矢量,让它的电角度按斜坡从0Hz升到目标频率,电流幅值先给额定值左右,转速上来之后再逐渐收敛。这个阶段电机就像步进电机一样被"硬拖着"转起来。

I/F阶段观测器可以照常运行,但需要让PLL的角度初始化和I/F给定角对齐,否则切换瞬间角度突变,电流冲击大到可能直接跳闸。我通常加一个切换判断:估算转速进入设定范围、角度误差连续保持小值一段时间,比如50ms,再切到无感FOC闭环。切换后短暂把iq限幅降到70%,等电流平稳再放开,这个软着陆动作很值。

4.4 仿真结果看什么

仿真主要盯四条曲线:转速指令和估算转速的跟随、dq电流波形、磁链观测器提取的角度误差、ESO的扰动估计z2。角度误差在仿真里用真实转子角度和估计角度做差,这个是观测器设计的核心评价指标。

我用上面那组参数跑,结果大概是:稳定运行在1000rpm时,估算角度误差在±3°以内,稳态转速误差小于10rpm;在0.5s突加0.6N·m负载时,转速跌落约30rpm,80ms内恢复。角度误差在负载突变瞬间会突然变大一点,但如果观测器增益给够,两个电流周期内能拉回来。

5. 代码生成:从模型到芯片的落地路径

5.1 生成前的模型改造清单

"可生成代码"是这套方案很大的卖点,省去了手写C的移植工作,但Simulink模型不是随便画完就能生成嵌入式代码的。生成之前必须按这个清单过一遍:

检查项要求
求解器固定步长,优先选discrete模式
连续积分器全部替换为Discrete-Time Integrator或Unit Delay
Scope/To Workspace从代码生成路径里移除或放到独立子系统
MATLAB Function定长数组、无动态内存、无文件IO
数据类型控制器侧统一用single,必要时加Data Type Conversion
采样时间用Rate Transition明确跨速率关系,不靠继承

为什么要强制固定步长离散?因为嵌入式芯片没有连续求解器,连续积分器在代码生成时会变成笨重且不确定的近似,离散积分器才能保证每拍行为一致。数据类型统一用single,是因为大多数电机控制MCU是32位浮点,double运算慢一倍且寄存器耗得多。

5.2 Embedded Coder的配置步骤

我在模型配置里大致走这几步:

  1. Solver面板:Type选Fixed-step,Solver选discrete (no continuous states),固定步长填10μs或1e-4都可以,关键是步长要和模型里最小时钟周期匹配。
  2. Hardware Implementation面板:选目标处理器架构,比如某32位浮点ARM内核,字长32位,浮点单元支持浮点。
  3. Code Generation面板:System target file选ert.tlc,勾上Generate code only,Toolchain按实际编译链选,后面专门建一个脚本维护配置,用slbuild批量构建。
  4. Optimization面板:优先Execution speed,打开内联和死代码消除。
  5. 参数存储方式:需要在线标定的量(kp、ωo、b0、k1、k2)设成Tunable,外部模式调参就靠这个。

构建完成后生成的.c文件里会看到和模型对应的函数入口,控制器目录下还会自动生成接口头文件,把驱动层和算法层用函数调用接起来,比在中断服务函数里手写一整片FOC要清晰得多。

5.3 生成的代码怎么接到驱动任务上

生成代码本质上是把"算法函数"交给你,中断框架、ADC、PWM外设还是要自己接。我的做法是:写一个底层驱动接口,ADC完成转换后,在PWM中断入口调用电流环和观测器的step函数,返回占空比;另一个低频定时器中断里调用速度环LADRC的step函数。电流环每拍都要执行观测器和PLL,计算量不大,在常见的200MHz级浮点MCU上,这个10kHz任务占用大概几个微秒,余量很充足。

代码生成之后不要直接上机,先做SIL(软件在环)对比:用同一组输入激励,把模型仿真结果和生成代码仿真结果对齐,确认数值一致;再做PIL(处理器在环),把代码烧到真实芯片上,让电机模型在外部实时跑,确认时序和数据类型没问题。这一步能拦截掉绝大多数"仿真好好的,代码一跑就疯"的问题。

6. 实测排坑:我踩过的几个典型问题

6.1 低速时角度抖动

低速阶段反电动势小,信噪比差,角度抖是必然的,关键看抖到什么程度。我遇到过一次转速升到300rpm时角度误差在±8°来回跳,电流波形全是毛刺。排查下来主要有三个原因:一是电压重构用了指令值而不是实际占空比,死区和管压降没进去;二是Rs标定偏差大;三是PLL带宽太高,直接放大了磁链估计里的噪声。

解决思路是:电压用占空比实测值和母线电压重构,温度补偿表做好;把PLL带宽降下来;观测器的非线性矫正项k2适当加大,让磁链轨迹更贴住电流模型。实在压不下去,就把I/F切换到无感的转速阈值往上抬,让系统在反电动势够大的区域再接闭环。

6.2 切换瞬间过流/反转

疑难杂症多出在I/F切换无感的瞬间。一次现场测试中,切换瞬间母线电流直接冲到限幅,转速往下掉,明显是切换时刻估算角度和真实角度差得远。原因是PLL在I/F阶段被强制同步到给定角,但给定角和实际转子角之间一直有一个负载相关的功角差。切换条件只看转速不看角度差,就会带病上岗。

我把切换条件改成了"转速误差小于阈值且角度误差连续50ms小于5°",并且切换后电流限幅软着陆,问题明显缓解。调试无感最实用的一招是:样机上先留编码器,把观测器角度和编码器角度同时录进同一张图,观察整条切换过程的角度突变,等曲线彻底平滑了再拆传感器。

6.3 负载突变后恢复慢

无感系统加阶跃负载,如果转速跌落之后回得非常慢,先别急着加ESO带宽,按顺序查三处:速度环b0是否偏大、ESO的ωo是否被噪声逼得太低、电流环限幅是否先到顶。我遇到过ωo加不上去的情况,每次超过250rad/s速度就开始抖,最后发现是PLL速度信号里还有周期性噪声,给速度反馈前面加了一个简单一阶滤波,把噪声压下去之后ωo就能往上加,负载恢复时间一下子缩短了。

另外要注意,负载突变反映在观测器里是磁链角度突跳而不是速度突跳,磁链观测器收敛性和LADRC的扰动补偿是互相配合的。角度观测不稳,速度环再强也白搭,这个顺序别搞反。

6.4 生成的代码和仿真行为不一致

代码生成之后行为对不上仿真,绝大多数不是算法问题,而是采样时序和数据类型问题。最常见的是Rate Transition没有做缓冲,低速任务读到的角度是高速任务正在写的半旧数据;其次是single和double混用,某些中间量被截断到精度不够。用SIL模式跑数值一致性测试时,如果误差在1e-6量级,基本是纯浮点舍入问题,可以接受;如果误差到百分之几,基本就是采样延误或类型转换问题。

还有一类坑在参数存储。仿真里参数可以直接改,生成代码后如果参数是Tunable,外部模式可以通过通信接口在线改,否则只能改常量重新编译。所以b0、ωo这类常需要现场微调的参数,生成前一定要设置成Tunable存到参数工作区,别图省事写死。

最后再分享一个小习惯:我每次带编码器先做一套完整的"有感LADRC+无感观测器并行"实验,确认观测器角度和编码器角度在全工况下误差都在线之后,才拔掉编码器跑纯无感。这个并行验证阶段能筛掉至少一大半的疑难bug,值得多花几天时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询