第1步把占空比还原成真实电压再变换到α-β坐标系float32_t Vtemp Vdcbus * obj-voltage_sf; float32_t VphaseA Vtemp * (pVabc_pu-value[0] * 2.0f - pVabc_pu-value[1] - pVabc_pu-value[2]); float32_t VphaseB Vtemp * (pVabc_pu-value[1] * 2.0f - pVabc_pu-value[0] - pVabc_pu-value[2]); obj-Valpha VphaseA; obj-Vbeta (VphaseA VphaseB * 2.0f) * MATH_ONE_OVER_SQRT_THREE;把 PWM 占空比乘上直流母线电压和缩放系数得到实际的相电压幅值。利用三相占空比算出A、B相电压间接得到C相然后用 Clark 变换变成 α-β 坐标下的电压Valpha, Vbeta。第2步用电压驱动虚拟电机算出估计电流float32_t ValphaError obj-Valpha - obj-Ealpha - obj-Zalpha; float32_t VbetaError obj-Vbeta - obj-Ebeta - obj-Zbeta; obj-EstIalpha obj-Gdsmopos * ValphaError obj-Fdsmopos * obj-EstIalpha; obj-EstIbeta obj-Gqsmopos * VbetaError obj-Fqsmopos * obj-EstIbeta;2.1 核心公式解释新估计电流 G × 净电压 F × 旧估计电流这其实是一个一阶低通滤波器用来模拟电感上的电流响应同时避免纯积分带来的漂移问题。电机α轴电压方程忽略电阻vα Ls * diα/dt eα也就是diα/dt (vα - eα) / Ls为了估计电流离散化得到最直观的纯积分i[k] i[k-1] (Ts / Ls) * (v[k] - e[k])但是纯积分有一个致命弱点任何微小直流偏置都会被持续累积导致输出漂移到无穷。所以实际工程中用一阶低通滤波器来近似积分它的差分方程是y[k] b * x[k] a * y[k-1]其中b是输入系数a是反馈系数且a b 1保证稳态增益为1。这个滤波器既有积分作用又能自动泄放偏置永不漂移。对比代码x[k]就是ValphaError净电压输入y[k]就是EstIalpha估计电流输出G对应于bF对应于aG 和 F 与电机参数的关系将连续域方程用后向欧拉法离散并加上低通特性后可推导出G (Ts / Ls) * (1 - α)F 1 - G这里的α是一个微小遗忘因子或低通参数确保数值稳定。G决定了电流每一步能变化多快F则让旧电流值保留下来。如果G过大估计电流会震荡过小则响应慢。正确的G,F搭配让虚拟电机的动态特性逼近真实电机。总结这一行它用电压输入的一阶低通代替了纯积分计算出平滑且无漂移的估计电流。2.2 净电压ValphaError的构成净电压 真实电压 - 估计反电动势 - 滑模修正项 Z减Ealpha我们模型里对反电动势的最佳猜测。减Zalpha后续根据电流误差算出的“暴力矫正信号”用来把估计电流硬拉向真实电流。这个被扭曲的净电压驱动虚拟电机使估计电流迅速跟上真实电流。第3步算电流误差产生大修正力 Zfloat32_t IalphaError obj-EstIalpha - pIabVec-value[0] * obj-current_sf; float32_t IbetaError obj-EstIbeta - pIabVec-value[1] * obj-current_sf; obj-Zalpha MATH_sat(IalphaError, obj-E0, -obj-E0) * obj-Kslide; obj-Zbeta MATH_sat(IbetaError, obj-E0, -obj-E0) * obj-Kslide;用估计电流减去真实电流得到误差。误差经过饱和限幅±E0后乘以大增益Kslide产生 Z。Z 的作用以超大的反馈增益强行把估计电流拽向真实电流使电流误差几乎为零。第4步低通滤波提取平滑反电动势低通滤波器的第二次应用obj-Ealpha obj-Ealpha obj-Kslf * (obj-Zalpha - obj-Ealpha); obj-Ebeta obj-Ebeta obj-Kslf * (obj-Zbeta - obj-Ebeta);扫盲低通滤波器的技术实现这里又是一个一阶低通滤波器但形式略有不同y[k] y[k-1] K * (x[k] - y[k-1])这个式子等价于y[k] K * x[k] (1 - K) * y[k-1]它在做什么把 Z高频开关信号一会儿正一会儿负平滑成 E缓慢变化的反电动势。Kslf 的作用决定了滤波器的截止频率。Kslf 越小滤波越狠输出越平滑但相位滞后越大。这里的 Kslf 正是基于这个原理设计的。为什么用低通滑模控制理论指出当电流误差被强制为零时开关信号 Z 的平均值就等于真实反电动势。低通滤波器恰好能提取平均值滤掉开关造成的高频抖动得到干净的 Eα、Eβ。第5步角度补偿与锁相环提取速度利用转速参考和滤波器系数算出相位补偿角抵消低通滤波器带来的延迟。将 Eα,Eβ 用补偿后的角度做 Park 变换得到 Ed, Eq。通过 PLL 的 PI 控制器强制 Ed → 0输出速度修正量。第6步速度滤波与角度积分对 PLL 输出速度进行移动平均和一阶低通滤波得到最终的速度估计speedFlt。将速度乘以控制周期累加到角度积分器theta上并归一化到 0~1 范围1 对应 360°。转换为弧度thetaEst±π 范围供 FOC 使用。数据流动全景图实测电压 → 净电压(减去E和Z) → 低通虚拟电机 → 估计电流 ↓ 真实电流 → 误差计算 → 饱和大增益 → Z(高频修正) ↓ 低通滤波 Z → E(平滑反电动势) ↓ 补偿PLL → 速度修正量 → 滤波 → 速度 ↓ 积分 → 角度记忆点这个观测器先“暴力”把自己虚拟电机的电流硬拉到真实电流Z然后从暴力修正力 Z 的平均值里“滤”出反电动势E再用锁相环从 E 里解出位置和速度。两个一阶低通滤波器贯穿其中一个模拟电流动态并防漂移一个从开关噪声中提炼反电动势。代码void ESMO_run(ESMO_Handle handle,float32_t Vdcbus, MATH_vec3 *pVabc_pu, MATH_vec2 *pIabVec) { ESMO_Obj *obj (ESMO_Obj *)handle; // Scale the incomming modulation functions with the DC bus voltage value // and calculate the 3 Phase voltages float32_t Vtemp Vdcbus * obj-voltage_sf; float32_t VphaseA Vtemp * (pVabc_pu-value[0] * 2.0f - pVabc_pu-value[1] - pVabc_pu-value[2]); float32_t VphaseB Vtemp * (pVabc_pu-value[1] * 2.0f - pVabc_pu-value[0] - pVabc_pu-value[2]); // Voltage transformation (a,b,c) - (Alpha,Beta) obj-Valpha VphaseA; obj-Vbeta (VphaseA VphaseB * 2.0f) * MATH_ONE_OVER_SQRT_THREE; // Sliding mode current observer float32_t ValphaError obj-Valpha - obj-Ealpha - obj-Zalpha; float32_t VbetaError obj-Vbeta - obj-Ebeta - obj-Zbeta; obj-EstIalpha obj-Gdsmopos * ValphaError obj-Fdsmopos * obj-EstIalpha; obj-EstIbeta obj-Gqsmopos * VbetaError obj-Fqsmopos * obj-EstIbeta; // Current errors float32_t IalphaError obj-EstIalpha - pIabVec-value[0] * obj-current_sf; float32_t IbetaError obj-EstIbeta - pIabVec-value[1] * obj-current_sf; // Sliding control calculator obj-Zalpha MATH_sat(IalphaError, obj-E0, -obj-E0) * obj-Kslide; obj-Zbeta MATH_sat(IbetaError, obj-E0, -obj-E0) * obj-Kslide; // Sliding control filter - back EMF calculator obj-Ealpha obj-Ealpha obj-Kslf * (obj-Zalpha - obj-Ealpha); obj-Ebeta obj-Ebeta obj-Kslf * (obj-Zbeta - obj-Ebeta); // arc tangent of src radians obj-Ealphaq31 (obj-speedRef * obj-offsetSF) *2147483647; obj-Ebetaq31 obj-Kslf *2147483647; obj-thetaElec MCM_PhaseComputation(obj-Ealphaq31, obj-Ebetaq31); obj-thetaOffset (float32_t)obj-thetaElec/32767; obj-thetaPll (obj-theta obj-thetaOffset)* MATH_TWO_PI; if(obj-thetaPll MATH_PI) { obj-thetaPll - MATH_TWO_PI; } else if(obj-thetaPll (-MATH_PI)) { obj-thetaPll MATH_TWO_PI; } float32_t pllSine arm_sin_f32(obj-thetaPll); float32_t pllCosine arm_cos_f32(obj-thetaPll); obj-Ed obj-Ealpha * pllCosine obj-Ebeta * pllSine; obj-Eq obj-Ebeta * pllCosine - obj-Ealpha * pllSine; obj-Eq_mag __sqrtf(obj-Ealpha * obj-Ealpha obj-Ebeta * obj-Ebeta); //0.1591549431 1/6.28 (1/(2*PI()) float32_t thetaErrSF obj-thetaErrSF; if(obj-Eq 0.0f) { thetaErrSF -obj-thetaErrSF; } obj-thetaErr obj-Ed * thetaErrSF / obj-Eq_mag; // integral term obj-pll_ui (obj-pll_Ki * obj-thetaErr) obj-pll_ui; // control output obj-pll_Out MATH_sat((obj-pll_Kp * obj-thetaErr obj-pll_ui), obj-pll_Umax, obj-pll_Umin); obj-speedEst (obj-pll_Out obj-speedEst) * 0.5f; // low pass filter for estimation speed obj-speedFlt obj-lpf_b0 * obj-pll_Out obj-lpf_a1 * obj-speedFlt; // speed integration to get the rotor angle obj-theta obj-theta obj-speedFlt * obj-thetaDelta; if(obj-theta 1.0f) { obj-theta - 1.0f; } else if(obj-theta -1.0f) { obj-theta 1.0f; } obj-thetaEst obj-theta * MATH_TWO_PI; if(obj-thetaEst MATH_PI) { obj-thetaEst - MATH_TWO_PI; } else if(obj-thetaEst (-MATH_PI)) { obj-thetaEst MATH_TWO_PI; } return; } // end of ESMO_run() function