做电机控制时间久了,难免会和“无位置传感器”这几个字打交道。省掉编码器或旋变之后,成本降了,机械结构也简单了,但代价是转子位置和速度只能靠算法去“猜”。猜的方法很多,有滑模观测器、高频注入、模型参考自适应,而我最近一个项目里用的是扩展卡尔曼滤波观测器,也就是常说的EKF,目标芯片是STM32,算法模型在Simulink里搭好之后自动生成C代码,再集成到永磁同步电机(PMSM)的FOC驱动工程里。整套流程走下来,最大的感受是:EKF本身并不神秘,真正麻烦的是模型搭建、参数校准和代码生成这些“脏活累活”。
这篇文章我会把这套方案从原理到落地完整拆开,包括状态方程怎么列、Simulink模型怎么搭、Q/R矩阵怎么调、Ke(反电动势常数)对估计结果的影响,以及从仿真模型到STM32真实运行之间会踩的坑。内容面向正在做无感FOC的嵌入式工程师,也适合刚接触观测器的研究生或DIY玩家参考。
1. 方案选型:为什么是EKF观测器
1.1 无位置传感器控制的基本盘
先梳理一下无感FOC的原理。FOC矢量控制的核心是坐标变换,而坐标变换必须要知道转子磁极的电角度θe和电角速度ωe。有传感器的时候,编码器直接告诉你这两个量;无传感器的时候,算法只能依赖电流、电压和电机参数去推算。
PMSM的定子电压方程里有反电动势项,而反电动势的大小和方向恰好包含了转子的速度与位置信息。比如表贴式PMSM在αβ静止坐标系下可以写成:
vα = Rs·iα + Ls·diα/dt - ψf·ωe·sinθe vβ = Rs·iβ + Ls·diβ/dt + ψf·ωe·cosθe这里面sinθe和cosθe是转子位置的函数,ωe是转速,ψf是转子磁链。也就是说,只要能从电压电流里准确地提取出反电动势的幅值和相位,就能反推速度和角度。但是直接对反电动势做反正切或锁相环,在噪声大的场合效果很差,这时就需要一个既能滤波又能估计状态的观测器。
EKF的思路是把电机的电流、转速、电角度全部放进一个状态向量里,用电机模型做一步预测,再用实测电流做校正。它不直接求反电动势,而是把位置和速度当成状态量一步步“逼近”真值。这种方法的优势在于:对测量噪声有统计意义上的平滑能力,而且同时输出速度和角度,不需要再做速度微分或额外的角度跟踪器。
1.2 EKF对比其他观测器:各自的家底
工程上常用的观测器大致有这几种:
| 观测器类型 | 适用场景 | 低速表现 | 中高速表现 | 调试难度 | 计算量 |
|---|---|---|---|---|---|
| 滑模观测器 | 表贴式/内置式PMSM | 较差 | 好,但抖振明显 | 低 | 低 |
| 高频注入法 | 内置式凸极PMSM | 很好 | 反而受限 | 高 | 中 |
| 模型参考自适应 | 各类PMSM | 一般 | 好 | 中 | 中 |
| 龙贝格观测器 | 线性模型较好的场合 | 一般 | 好 | 中 | 低 |
| 扩展卡尔曼滤波 | 表贴式/内置式PMSM | 较差 | 好,噪声抑制能力强 | 较高 | 高 |
从表格可以看出一条脉络:低速性能最好的高频注入依赖电机凸极性,而且高频信号设计复杂;滑模观测器结构简单但对参数和噪声敏感,抖振是一个绕不开的问题;EKF的模型是非线性的,对负载突变和测量噪声的处理更“温和”,代价是计算量大,Q/R矩阵调起来费时间。
我选择EKF还有一个现实原因:项目用的电机是表贴式PMSM,凸极性很弱,高频注入基本发挥不出来,而目标MCU有硬件浮点单元,跑4维EKF的实时性压力可控。如果你手头是内置式电机且要求零速满载启动,那EKF未必是最合适的答案,建议考虑高频注入或混合方案。
1.3 从状态方程到可观测性
EKF需要一个数学模型作为预测基础。这里以表贴式PMSM为例,忽略磁路饱和,并把负载转矩视为恒定扰动,取状态向量:
x = [iα, iβ, ωe, θe]ᵀ输入量是静止坐标系电压uα、uβ,测量量是αβ电流。完整状态方程如下:
diα/dt = -(Rs/Ls)·iα + (ψf/Ls)·ωe·sinθe + (1/Ls)·uα diβ/dt = -(Rs/Ls)·iβ - (ψf/Ls)·ωe·cosθe + (1/Ls)·uβ dωe/dt = (1.5·p²·ψf/J)·(-iα·sinθe + iβ·cosθe) - (p/J)·TL dθe/dt = ωe注意式子里θe是电角度,ωe是电角速度,p是极对数。如果仿真和代码里混用了机械角度和电角度,很容易出现速度估计对但角度切换滞后半圈之类的诡异现象,这是后面调试排查的重灾区。
从可观测性角度看,EKF在零速附近是天然的“盲区”。因为当ωe接近0时,反电动势几乎消失,电流方程里携带转速和角度信息的项趋向于零,单靠电流是推不出角度的。这一点必须在方案设计阶段就接受,后续启动策略只能绕开零速,而不是指望EKF解决零速。
2. Simulink模型搭建:把电机和EKF放进一张图里
2.1 电机本体模型怎么建才真实
很多初学者喜欢直接拖Simulink库里的PMSM模型,方便是真方便,但这里有个坑:库模型的内部实现未必和你的EKF假设完全一致,比如是否考虑了齿槽转矩、铁损、磁饱和,坐标变换正方向定义也可能不同。你拿库模型做EKF验证,大概率会“自洽”得过头,换到真机就露馅。
我建议电机本体模型在单独一个子系统里手写。用两个环节就够了:一个是电机的电流微分方程,一个是机械运动方程。实现起来可以直接用Gain、Sum、Product、Trigonometric Function这些基础模块搭,也可以用MATLAB Function写,但注意手写电机模型不要参与代码生成,它是运行在PC仿真环境里的“假电机”,跟MCU上的EKF算法完全分开。
电机模型的参数表建议做成MATLAB脚本里的基础变量,比如Rs=0.4Ω、Ls=1.2mH、ψf=0.05Wb、极对数p=4、转动惯量J=0.00012kg·m²。这样无论是仿真还是后续生成代码,都用同一份参数,避免在两处维护。
2.2 EKF观测器模块的内部结构
EKF观测器是整个控制模型的核心,我把它单独封装成一个子系统,对外接口非常干净:
- 输入:uα、uβ、iα、iβ
- 输出:θe、ωe
子系统内部按预测、线性化、增益计算、校正四步组织。离散化采用一阶欧拉,采样周期Ts也就是电流环中断周期,通常取1e-4秒(10kHz)或者5e-5秒(20kHz)。
卡尔曼滤波的五个核心公式:
预测:x̂ₖ₋ = x̂ₖ₋₁₊ + Ts·f(x̂ₖ₋₁₊, uₖ₋₁) 线性化:Fₖ = I + Ts·∂f/∂x 协方差预测:Pₖ₋ = Fₖ·Pₖ₋₁₊·Fₖᵀ + Q 增益:Kₖ = Pₖ₋·Hᵀ·(H·Pₖ₋·Hᵀ + R)⁻¹ 校正:x̂ₖ₊ = x̂ₖ₋ + Kₖ·(zₖ - h(x̂ₖ₋))这里H是测量矩阵,因为测量量直接就是αβ电流,所以H=[1 0 0 0; 0 1 0 0],非常简单。z_k是实测电流。
实现方式有两种。一种是用Gain、Sum、Matrix Multiply等模块搭,直观但模型会很大,改动麻烦。另一种是用MATLAB Function模块写这段逻辑,然后在模块设置里勾选支持代码生成。我自己更推荐第二种,核心算法收敛成几十行文本,比一堆连线好维护得多,代码生成也没问题。
2.3 Q与R矩阵:人人都会抄,但很少有人调明白
Q矩阵是过程噪声协方差,代表你对模型和输入的信任程度;R矩阵是测量噪声协方差,代表你对电流采样值的信任程度。这两个矩阵没有“标准答案”,必须针对具体电机和电流采样电路来调。
一个实用的起点是:先估R,再调Q。R矩阵可以根据ADC采样噪声来定,比如把电机堵转、通一个固定占空比,在电流静止场景下采集一组电流数据,算出方差,这个方差就是R(1,1)和R(2,2)的量级参考。如果ADC噪声在±0.05A内波动,那么R取大约(0.05)²到(0.1)²这个数量级就合理。
Q矩阵的调节经验是:Q(1,1)和Q(2,2)对应电流模型误差,通常给得很小,因为它们本身是测量方程里的状态,模型又比较准;Q(3,3)对应转速模型的误差,负载突变频繁的场合要适当加大;Q(4,4)对应角度漂移,如果启动后角度跟不上,可以暂时把Q(4,4)调大加速收敛,但稳态后必须降回来,否则角度抖动会比较明显。
调参的技巧是用平台上的编码器作为“标准答案”,把EKF估计出来的角度和编码器实测角度同时抓出来做对比。先把Q调大让估计快速收敛,再逐步减小Q,直到角度误差曲线稳定在几个度以内,最后再精细调R来压制毛刺。
3. Ke在EKF里的戏份:磁链常数的来龙去脉
3.1 Ke与转子磁链是什么关系
标题里的Ke,放在PMSM语境下最常指反电动势常数。它和电机模型里的ψf直接相关,但两者不是同一个东西。ψf是转子磁链,单位是韦伯(Wb);Ke是反电动势常数,常见单位是V/(rad/s)或V/(krpm)。两者之间隔着一个绕组换算系数,不同文献的定义还经常不一致。
举个例子,一台小功率伺服电机,厂商给Ke=0.15V/(rad/s),这代表每1000rpm机械转速产生约15.7V的线反电动势幅值。如果在Simulink模型里需要填ψf,不能直接把Ke填进去,得根据电机三相绕组接法做换算。常见的错误就是把Ke当ψf用,结果反电动势项整体放大,EKF在高速区出现固定角度偏差。
| 参数 | 常见单位 | 典型值 | 说明 |
|---|---|---|---|
| ψf | Wb | 0.05 | 转子磁链,建模直接使用 |
| Ke | V/(rad/s) | 0.05 | 单相反电动势常数,与ψf近似相等(视定义) |
| Ke | V/(krpm) | 5.24 | 工程习惯单位,换算时注意转速单位 |
厂家给的Ke通常是线反电动势有效值或幅值,换算的时候必须把极对数、绕组Y接/D接、线/相都搞清楚。最稳妥的做法是拿电机做一次反拖试验,实测数据来定Ke或ψf,别只依赖铭牌。
3.2 Ke误差会怎样破坏估计结果
EKF模型里的反电动势项是ψf乘以转速乘以sin/cos位置。如果填进去的Ke偏大,预测出的反电动势就会比实际大。卡尔曼增益在计算残差时会看到电流预测和实测之间的系统性偏差,为了补偿这个偏差,它会让角度估计往减小预测反电动势的方向偏,表现出来就是稳态角度误差,负载越大偏差越明显。
如果Ke偏小,情况正好相反,反电动势项太弱,EKF对位置的敏感度下降,同样会导致角度偏差,而且动态跟随会变差,负载突变时容易出现转速估计滞后。
更麻烦的情况是Ke误差和Q/R调参错误被“互相抵消”。我在调试时就遇到过Ke偏大一点,但把Q(4,4)也调大,结果稳态角度误差看起来正常,一旦负载突加,角度瞬间甩飞。所以参数辨识的优先级永远高于调Q/R。先确认Ke准确,再谈噪声矩阵。
3.3 工程中如何校准Ke
离线标定Ke的思路很简单:让电机以已知转速旋转,断电或给零电流,测量相线反电动势。具体做法可以保留编码器,用另一台电机把被测电机反拖到某个固定转速,用示波器或者电压探头测三相线电压的峰峰值。比如1000rpm下测得线电压峰峰值为Vpp,那么相电压幅值Vph=Vpp/2(Y接时),进而算出Ke=Vph/ω。
这个实验在实验室里很容易搭,关键点有三个:一是转速要稳定,最好用闭环反拖;二是用高阻抗探头,减少负载效应;三是记录温度,永磁体的磁链受温度影响明显,常温下标定的数据在电机跑热之后会有几个百分点的误差,这属于正常现象。
如果想要在线辨识Ke,可以把ψf扩展成EKF的状态向量的一部分,做联合估计。理论上可行,但实际运行时ψf和速度、位置存在很强的耦合,低速或匀速段很容易漂移。我的建议是能用离线标定解决的问题,别花大力气做在线辨识。
4. STM32自动代码生成:从仿真到真机要迈过的坎
4.1 模型配置与代码生成设置
Simulink自动代码生成这件事,用好了是真省力,但配置不对也是真出坑。首先要确认模型是离散的,求解器选择固定步长、离散模式,步长和实际控制周期保持一致,比如5e-5秒或1e-4秒。如果模型里混入了连续积分模块,生成出来的代码会在每个采样周期里做连续状态的近似更新,逻辑会变复杂,实时性变差。
代码生成目标我选择的是Simulink的嵌入式C代码生成器,而不是默认的仿真目标。关键设置包括:
- 求解器选择固定步长离散;
- 代码生成里面把数据类型显式定义成single,避免double在无FPU内核上跑出巨大计算量;
- 生成函数名、文件前缀改成模块名,方便集成到现有工程;
- 勾选“生成代码只包含当前子系统”或者用模型引用方式,把EKF观测器独立生成。
我习惯的工作流是用STM32CubeMX先初始化时钟、定时器、ADC和PWM,生成底层HAL工程,然后手动把Simulink生成的核心算法C函数嵌入到中断服务函数里。这套方法最灵活,不依赖特定IDE。如果你不想碰手工集成,也可以用硬件支持包做一键部署,但底层外设配置和中断优先级被工具绑死,排查问题反而不方便。
4.2 集成PWM、ADC与相电流重构
真正跑起来,ADC采样和PWM的同步是最容易翻车的环节。电流环在PWM周期中段触发ADC转换,这个时刻相电流相对平稳,受死区和开关噪声影响最小。ADC采样完成后进入中断,中断里先做Clarke变换,再调用EKF预测和校正,最后跑电流环PI,更新PWM占空比。
相电流重构也是一门学问。硬件上一般采样两相电流,第三相通过i_c=-i_a-i_b计算。采样点必须避开PWM死区和开关暂态,否则那一瞬间的电流会包含严重的尖峰噪声,这些噪声直接进EKF的测量量,会让角度估计带上明显的纹波。如果发现EKF输出在固定频率附近抖动,大概率是采样时刻不对,而不是滤波器参数问题。
EKF需要的输入电压uα、uβ一般不用额外传感器测,直接用母线电压和三相占空比重构。但这里要注意:占空比是下一周期要输出的值,还是本周期正在输出的值,时序错了角度就会差固定角度。而且电压重构必须做死区补偿和管压降修正,尤其在低速重载时,不补偿的电压误差会产生很大的反电动势估算偏差。
无位置传感器的启动顺序,我的建议分为三段:第一段是预定位,给一个固定电压矢量把转子锁到已知位置;第二段是开环强拉,转速给定从零缓慢上升,电流环按I-f方式控制;第三段是当转速超过额定值5%左右时,把角度和速度来源从开环给定切换成EKF输出。切换时要做角度平滑过渡,让两个角度在短时间内融合切换,直接硬切容易电流冲击。
4.3 实时性与采样周期
EKF的实时性压力主要集中在矩阵运算和三角函数。4维状态向量意味着F矩阵是4x4,协方差矩阵P是4x4,每一次迭代要做两次4x4矩阵乘法和一次矩阵求逆(虽然实际上利用对称性可以减少一半乘法)。再加上sin和cos计算,整体估算下来,在一颗带单精度FPU的STM32上,EKF核心计算量大约是十微秒到几十微秒之间。
按10kHz中断周期,整个中断服务函数只有100μs的时间预算。留给EKF的往往只有20~40μs。如果MCU没有FPU,所有浮点运算都是软浮点,这个预算基本不够,必须把数据类型改成定点或者提前用查表法做正弦余弦。所以选型阶段别只看主频,FPU和三角函数指令的支持情况同样关键。
如果实测中断里时间超了,优先做三件事:第一,把EKF状态更新里的矩阵乘法用展开式计算,让编译器省掉多重循环;第二,用sinf/cosf或者查表代替double版本;第三,把电流环执行频率和EKF执行频率解耦,比如电流环20kHz,EKF 10kHz,EKF隔一拍执行一次,也能降低峰值负载。
5. 调试实录:实测中踩过的坑和排查方法
5.1 EKF不收敛或转速发散怎么查
我见过最多的不收敛症状是上电后转速估计值乱跳,或者角度直接转圈。这种问题九成不是卡尔曼增益的问题,而是符号和方向的问题。排查顺序应该是:
- 检查Clarke变换和Park变换的正方向一致性;
- 确认EKF里使用的是电角度还是机械角度;
- 用一个开环强拉工况,把uvw波形和EKF估计的角度放在同一张图里看,确认角度和真实反电动势方向是否对齐;
- 确认电流采样极性和电机相序,如果两个电流探头极性接反,EKF会稳定地给出错误结果。
在仿真阶段就建议故意留一个“标准答案”通道:给电机模型接一个理想编码器,EKF的估计值被拉出来做对比,所有时序问题在这个阶段暴露,比在真机示波器上猜要快得多。
5.2 低速与零速工况怎么处理
前面提到EKF在零速附近可观测性变差,这是物理规律,不是调参能解决的。低速时段建议用开环I-f启动顶着,当转速超过一定阈值再切换到EKF闭环。具体切换阈值和负载有关,我一般在额定转速的3%到5%之间加一个滞回,切换时把角度差限定在30度以内,防止切换瞬间电流突变。
如果负载要求零速满力矩保持,光靠EKF就撑不住了。这时候可以保留一个低频的编码器或者采用高频注入做混合观测器,但这会显著增加系统的复杂度和调试成本。先问清楚需求是关键。
5.3 代码生成后浮点与数据类型问题
Simulink模型里默认数据类型是double,生成到STM32上后,如果内核没有FPU,double类型会调用软浮点库,计算量可能是单精度浮点的数十倍。我的习惯是在模型里用仿真缩放功能把所有算法模块的数据类型统一为single,ADC和PWM接口用定点int16到single的转换模块做显式转换,让代码生成明显更简洁。
还有一个小坑是Q矩阵和R矩阵在Simulink里直接赋值时精度够,但生成代码后如果编译优化开了,某些常量会被编译器提前计算,出现极小的矩阵项变成0的情况,导致协方差矩阵奇异。处理办法是给Q和R对角元加一个非常小的下限,比如1e-10,防止矩阵病态。
最后再分享一条实测经验:调EKF时别把期望定得太高,无感FOC在低速区的性能天花板就是比带编码器差一截。只要中高速段角度误差稳定在几度以内、负载突变不丢步,这个系统在工程上已经可以交付了。观测器的调参,与其反复抄网上的Q/R参考值,不如静下心把电机参数、电流采样、时序方向这三件事钉死,后面一切都顺了。