ESP32无感滑模FOC深度学习文档
基于灯哥DengFOC V4例程4:无感滑模完整控制例程
包含滑模观测器、锁相环、SVPWM深度解析
目录
- 无感FOC基础理论
- 滑模观测器(SMO)深度解析
- 锁相环(PLL)深度解析
- SVPWM深度解析
- 代码架构分析
- 核心模块详解
- 调参指南
- 常见问题与调试
1. 无感FOC基础理论
1.1 什么是无感FOC?
FOC(Field Oriented Control,磁场定向控制)是一种高效的电机控制方法。无感FOC是指不使用位置传感器(如编码器、霍尔传感器),而是通过观测器算法估算转子位置和速度的控制方式。
1.2 PMSM电机数学模型
无感FOC的核心是利用电机的反电动势来估算转子位置。
电压方程(α-β坐标系)
1 2
| Uα = Rs·Iα + Ls·(dIα/dt) + Eα Uβ = Rs·Iβ + Ls·(dIβ/dt) + Eβ
|
其中:
Uα, Uβ:定子电压
Iα, Iβ:定子电流
Rs:相电阻
Ls:相电感
Eα, Eβ:反电动势(关键!)
反电动势与转子位置的关系
1 2
| Eα = -Ke·ω·sin(θ) Eβ = Ke·ω·cos(θ)
|
关键结论:如果我们能准确估算出反电动势,就能计算出转子位置!
1.3 无感FOC控制框图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| ┌─────────────────────────────────────────────────────────────────┐ │ 无感FOC控制框图 │ └─────────────────────────────────────────────────────────────────┘
目标速度/目标电流 │ ↓ ┌──────────┐ │ 速度环 │ → Id_ref (通常为0), Iq_ref │ PID │ └──────────┘ │ ↓ ┌──────────┐ │ 电流环 │ → Ud, Uq │ PID │ └──────────┘ │ ↓ ┌──────────────────────────────────────────┐ │ Park逆变换 (dq→αβ) │ │ Uα = Ud·cos(θ̂) - Uq·sin(θ̂) │ │ Uβ = Ud·sin(θ̂) + Uq·cos(θ̂) │ └──────────────────────────────────────────┘ │ ↓ ┌──────────────────────────────────────────┐ │ SVPWM调制 │ │ 生成三相PWM占空比 │ └──────────────────────────────────────────┘ │ ↓ ┌──────────────────────────────────────────┐ │ 电机 │ │ 产生实际电流 Ia, Ib │ └──────────────────────────────────────────┘ │ ↓ ┌──────────────────────────────────────────┐ │ Clarke变换 (ABC→αβ) │ │ Iα = Ia, Iβ = (Ia+2Ib)/√3 │ └──────────────────────────────────────────┘ │ ↓ ┌──────────────────────────────────────────┐ │ Park变换 (αβ→dq) │ │ 使用估算角度θ̂进行变换 │ │ Id = Iα·cos(θ̂) + Iβ·sin(θ̂) │ │ Iq = Iβ·cos(θ̂) - Iα·sin(θ̂) │ └──────────────────────────────────────────┘ │ ↓ ┌──────────────────────────────────────────┐ │ 滑模观测器 (SMO) │ │ 输入: Uα,Uβ, Iα,Iβ │ │ 输出: 估算角度θ̂, 估算速度ω̂ │ └──────────────────────────────────────────┘ │ └──→ 反馈到Park变换和Park逆变换
|
2. 滑模观测器(SMO)深度解析
2.1 滑模控制理论基础
2.1.1 什么是滑模控制?
滑模控制(Sliding Mode Control)是一种非线性控制策略,其核心思想是:
- 设计滑模面:定义一个切换函数 s(x) = 0
- 设计控制律:使系统状态在有限时间内到达滑模面
- 保持滑动:一旦到达滑模面,控制律使系统状态保持在滑模面上运动
2.1.2 滑模面的物理意义
在电机控制中,我们定义滑模面为电流误差:
1 2
| sα = Îα - Iα (α轴电流误差) sβ = Îβ - Iβ (β轴电流误差)
|
滑模面的物理意义:
- 当 s = 0 时,估算电流等于实际电流
- 当 s ≠ 0 时,存在误差,需要通过控制律消除
2.1.3 滑模存在条件
滑模存在的充分条件(李雅普诺夫稳定性):
1 2
| V = (1/2)·s² (定义能量函数) dV/dt = s·(ds/dt) < 0 (能量必须减小)
|
2.2 滑模观测器完整推导
2.2.1 电机数学模型(连续域)
在α-β坐标系下,PMSM电机的电压方程为:
1 2
| dIα/dt = -(Rs/Ls)·Iα + (1/Ls)·Uα - (1/Ls)·Eα dIβ/dt = -(Rs/Ls)·Iβ + (1/Ls)·Uβ - (1/Ls)·Eβ
|
整理得:
其中:
1 2 3 4 5
| I = [Iα, Iβ]ᵀ (电流向量) U = [Uα, Uβ]ᵀ (电压向量) E = [Eα, Eβ]ᵀ (反电动势向量) A = -(Rs/Ls)·I (系统矩阵) B = (1/Ls)·I (输入矩阵)
|
2.2.2 构造观测器
我们构造一个与实际电机并行的观测器模型:
1 2
| dÎα/dt = -(Rs/Ls)·Îα + (1/Ls)·Uα - (1/Ls)·Êα dÎβ/dt = -(Rs/Ls)·Îβ + (1/Ls)·Uβ - (1/Ls)·Êβ
|
关键:观测器中的 Êα, Êβ 是通过滑模控制律估算的,而不是实际测量值!
2.2.3 定义电流误差(滑模面)
1 2
| eα = Îα - Iα eβ = Îβ - Iβ
|
用向量形式:
2.2.4 误差动力学方程
用观测器方程减去实际方程:
1 2 3 4 5
| deα/dt = dÎα/dt - dIα/dt = -(Rs/Ls)·(Îα - Iα) - (1/Ls)·(Êα - Eα) = -(Rs/Ls)·eα - (1/Ls)·(Êα - Eα)
deβ/dt = -(Rs/Ls)·eβ - (1/Ls)·(Êβ - Eβ)
|
2.2.5 设计滑模控制律
我们的目标是:使 e = 0,即估算电流等于实际电流。
使用等速趋近律设计控制律:
1 2
| Êα = h·sat(eα, ε) Êβ = h·sat(eβ, ε)
|
其中:
h:滑模增益(必须足够大以保证滑模存在)
sat():饱和函数(替代符号函数以减少抖振)
2.2.6 为什么控制律能估算反电动势?
当系统进入滑动模态后(e = 0,de/dt = 0),从误差动力学方程:
1
| 0 = -(Rs/Ls)·0 - (1/Ls)·(Êα - Eα)
|
解得:
结论:在滑模状态下,估算的反电动势收敛于真实的反电动势!
2.3 饱和函数 vs 符号函数
2.3.1 符号函数(传统滑模)
1 2 3
| sign(x) = { +1, x > 0 { 0, x = 0 { -1, x < 0
|
问题:在滑模面附近产生高频抖振(chattering)
2.3.2 饱和函数(改进滑模)
1 2 3
| sat(x, ε) = { +1, x > ε { x/ε, -ε ≤ x ≤ ε { -1, x < -ε
|
优势:在边界层内线性变化,减少抖振
2.3.3 代码实现
1 2 3 4 5 6
| float SMO::sat(float err, float limits) { if (err > limits) return 1; else if (err < -limits) return -1; else return err / limits; }
|
使用示例:
1 2
| Ealpha = h * sat(Ialpha_Err, 0.5f); Ebeta = h * sat(Ibeta_Err, 0.5f);
|
2.4 滑模增益h的选择
2.4.1 理论分析
滑模存在的条件:
1
| h > |Eα|_max 且 h > |Eβ|_max
|
即滑模增益必须大于最大反电动势。
2.4.2 工程经验
| 电机类型 |
推荐h值 |
说明 |
| 小型云台电机(2208) |
0.2 ~ 0.5 |
电感大,反电动势小 |
| 中型电机 |
0.5 ~ 1.0 |
平衡值 |
| 大型电机 |
1.0 ~ 2.0 |
电感小,反电动势大 |
2.4.3 h值调试技巧
1 2 3 4 5 6 7 8 9 10 11
| 现象1:电机不转,电流振荡 原因:h太小,滑模条件不满足 解决:h × 1.5,逐步增大
现象2:高频噪音,估算角度抖动 原因:h太大,过度反应 解决:h × 0.8,逐步减小
现象3:低速性能差 原因:低速时反电动势小,h相对太大 解决:使用自适应h或分段h
|
2.5 滑模观测器完整代码解析
2.5.1 数据结构定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| struct SMO_params { float Rs; float Ls; float h; float PLL_kp; float PLL_ki; float VF_acc; float VF_max_vel; float VF_uq_delta; };
class SMO { private: float Rs; float Ls;
float Ualpha, Ubeta; float Ialpha, Ibeta; float Est_Ialpha, Est_Ibeta; float Ialpha_Err, Ibeta_Err;
float h; float Ealpha, Ebeta; float Ealpha_flt, Ebeta_flt;
float Est_Theta; float Est_speed; float PLL_kp, PLL_ki;
uint32_t now_time; uint32_t last_time; float Ts;
public: SMO(SMO_params para); void SMO_closeloop(Cur_vol_s Cur_vol); float sat(float err, float limits); void SMO_position_estimate(); void PLL_calc(float alpha, float beta); };
|
2.5.2 核心函数1:滑模闭环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| void SMO::SMO_closeloop(Cur_vol_s Cur_vol) { now_time = micros(); Ts = (now_time - last_time) * 1e-6f; last_time = now_time;
if (Ts < 0 || Ts > 5e-3f) Ts = 1e-3f;
Ialpha = Cur_vol.I_alpha; Ibeta = Cur_vol.I_beta;
Ualpha = -sin(Est_Theta) * Cur_vol.Uq; Ubeta = cos(Est_Theta) * Cur_vol.Uq;
SMO_position_estimate(); }
|
代码说明:
- 采样周期Ts必须准确,影响欧拉积分精度
- Ud=0是最大转矩/安比特性的控制策略
- 使用估算角度Est_Theta进行Park逆变换
2.5.3 核心函数2:位置估算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| void SMO::SMO_position_estimate() {
Est_Ialpha += Ts * (-Rs / Ls * Est_Ialpha + 1 / Ls * (Ualpha - Ealpha)); Est_Ibeta += Ts * (-Rs / Ls * Est_Ibeta + 1 / Ls * (Ubeta - Ebeta));
Ialpha_Err = Est_Ialpha - Ialpha; Ibeta_Err = Est_Ibeta - Ibeta;
Ealpha = h * sat(Ialpha_Err, 0.5f); Ebeta = h * sat(Ibeta_Err, 0.5f);
Ealpha_flt = 0.1 * Ealpha_flt + 0.9 * Ealpha; Ebeta_flt = 0.1 * Ebeta_flt + 0.9 * Ebeta;
if (_dir >= 0) PLL_calc(Ealpha_flt, Ebeta_flt); else PLL_calc(-Ealpha_flt, -Ebeta_flt); }
|
代码说明:
- 欧拉法是一阶数值积分,精度适中但计算简单
- 滤波系数0.1/0.9可根据需要调整
- 方向判断确保正反转都能正常工作
2.5.4 核心函数3:饱和函数
1 2 3 4 5 6
| float SMO::sat(float err, float limits) { if (err > limits) return 1; else if (err < -limits) return -1; else return err / limits; }
|
函数特性:
1 2 3 4 5 6 7 8 9 10 11
| 输出 1 | ┌───────── | / | / | / 0 |-------+--------→ 误差 | / | / | / -1 ─────────┘ -limits +limits
|
2.6 滑模观测器信号流图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| ┌────────────────────────────────────────────────────────────────┐ │ 滑模观测器完整信号流 │ └────────────────────────────────────────────────────────────────┘
实际电机 观测器模型 ┌─────────┐ ┌─────────┐ Uα→ │ dIα/dt │ │ dÎα/dt │←─── Êα │ = f(I,U,E)│ │ = f(Î,U,Ê)│ └─────────┘ └─────────┘ │ │ ↓ Iα ↓ Îα └──────────┬──────────┘ ↓ ┌──────────┐ │ eα = Îα-Iα│ ← 滑模面 └──────────┘ │ ↓ ┌──────────┐ │ sat(eα,ε) │ └──────────┘ │ ↓ ┌──────────┐ │ × h │ └──────────┘ │ ↓ Êα ┌───────────────┐ │ 低通滤波 │ └───────────────┘ │ ↓ Eα_flt ┌───────────────┐ │ PLL │ └───────────────┘ │ ↓ θ̂ (估算角度)
|
2.7 滑模观测器稳定性分析
2.7.1 李雅普诺夫函数
定义能量函数:
2.7.2 能量变化率
1 2 3 4 5
| dV/dt = eα·(deα/dt) + eβ·(deβ/dt) = eα·[-(Rs/Ls)·eα - (1/Ls)·(h·sat(eα) - Eα)] + eβ·[-(Rs/Ls)·eβ - (1/Ls)·(h·sat(eβ) - Eβ)] = -(Rs/Ls)·(eα² + eβ²) - (1/Ls)·[eα·(h·sat(eα) - Eα) + eβ·(h·sat(eβ) - Eβ)]
|
2.7.3 滑模存在条件
当 |e| > ε(在饱和区)时,sat(e) = sign(e),则:
1
| e·(h·sat(e) - E) = h·|e| - e·E
|
如果 h > |E|_max,则:
1
| e·(h·sat(e) - E) > h·|e| - |e|·|E|_max = |e|·(h - |E|_max) > 0
|
因此:
1
| dV/dt = -(Rs/Ls)·|e|² - (1/Ls)·|e|·(h - |E|_max) < 0
|
结论:当 h > |E|_max 时,系统全局渐近稳定!
3. 锁相环(PLL)深度解析
3.1 PLL基本原理
3.1.1 什么是锁相环?
锁相环(Phase-Locked Loop,PLL)是一种相位反馈控制系统,能够:
- 跟踪输入信号的相位
- 输出与输入信号同频同相的信号
- 同时输出频率信息
3.1.2 为什么需要PLL?
在无感FOC中,我们从滑模观测器得到反电动势:
1 2
| Eα = -Ke·ω·sin(θ) Eβ = Ke·ω·cos(θ)
|
问题:如何从Eα、Eβ中提取角度θ?
方法1:直接计算
缺点:atan2计算量大,且对噪声敏感
方法2:PLL(推荐)
优点:
3.2 PLL数学模型
3.2.1 相位误差计算
利用三角恒等式:
1 2 3 4
| -Eα·cos(θ̂) - Eβ·sin(θ̂) = -(-Ke·ω·sin(θ))·cos(θ̂) - (Ke·ω·cos(θ))·sin(θ̂) = Ke·ω·[sin(θ)·cos(θ̂) - cos(θ)·sin(θ̂)] = Ke·ω·sin(θ - θ̂) ← 正弦定理
|
当角度误差较小时:
因此:
3.2.2 PLL控制框图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| ┌────────────────────────────────────────────────────────────┐ │ PLL控制框图 │ └────────────────────────────────────────────────────────────┘
Eα_flt, Eβ_flt │ ↓ ┌──────────────────┐ │ 相位误差计算 │ │ θe = -Eα·cos(θ̂) │ │ -Eβ·sin(θ̂) │ └──────────────────┘ │ ↓ θe (相位误差) ┌──────────────────┐ │ PI控制器 │ │ ω̂ = Kp·θe + │ │ Ki·∫θe·dt │ └──────────────────┘ │ ↓ ω̂ (估算速度) ┌──────────────────┐ │ 积分器 │ │ θ̂ = θ̂ + ω̂·Ts │ └──────────────────┘ │ ↓ θ̂ (估算角度) └───→ 反馈到相位误差计算
|
3.2.3 PLL传递函数
开环传递函数:
闭环传递函数(特征方程):
1 2
| 1 + G(s) = 0 s² + Kp·s + Ki = 0
|
这是典型的二阶系统!
3.2.4 与二阶系统对应
标准二阶系统:
对应关系:
1 2
| ωn² = Ki (自然频率平方) 2·ζ·ωn = Kp (阻尼比相关)
|
因此:
1 2
| ωn = √Ki ζ = Kp / (2·√Ki)
|
3.2.5 PLL参数设计
步骤1:选择自然频率ωn
- ωn越大,响应越快,但滤波效果差
- 推荐:ωn = 100~500 rad/s
步骤2:选择阻尼比ζ
- ζ = 0.707(临界阻尼,无超调)
- ζ = 1.0(过阻尼,响应慢但平滑)
步骤3:计算Kp、Ki
示例(ωn = 200, ζ = 0.707):
1 2
| Ki = 200² = 40000 Kp = 2 × 0.707 × 200 = 282.8 ≈ 300
|
3.3 PLL代码实现
3.3.1 PLL计算函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| void SMO::PLL_calc(float alpha, float beta) { Theta_err = -1 * alpha * cos(Est_Theta) + beta * sin(Est_Theta) * -1;
i_err += Ts * PLL_ki * Theta_err;
Est_speed = PLL_kp * Theta_err + i_err;
Est_speed_F = Est_speed_F * 0.9 + Est_speed * 0.1;
Est_Theta += Ts * Est_speed;
Est_Theta = _normalizeAngle(Est_Theta); }
|
3.3.2 关键点说明
1. 相位误差计算详解
1
| Theta_err = -alpha * cos(Est_Theta) - beta * sin(Est_Theta);
|
展开:
1
| Theta_err = -Eα·cos(θ̂) - Eβ·sin(θ̂)
|
假设实际角度为θ,代入反电动势公式:
1 2 3
| = -(-Ke·ω·sin(θ))·cos(θ̂) - (Ke·ω·cos(θ))·sin(θ̂) = Ke·ω·[sin(θ)·cos(θ̂) - cos(θ)·sin(θ̂)] = Ke·ω·sin(θ - θ̂) ← 正弦差公式
|
物理意义:
- 当 θ̂ = θ 时,Theta_err = 0
- 当 θ̂ < θ 时,Theta_err > 0
- 当 θ̂ > θ 时,Theta_err < 0
2. PI控制器详解
1 2
| i_err += Ts * PLL_ki * Theta_err; Est_speed = PLL_kp * Theta_err + i_err;
|
比例项:
积分项:
3. 角度积分
1
| Est_Theta += Ts * Est_speed;
|
物理意义:
1
| θ̂(k+1) = θ̂(k) + ω̂(k)·Ts
|
这是速度积分得到角度的基本公式。
3.4 PLL动态响应分析
3.4.1 阶跃响应
设初始角度误差为Δθ,则:
1
| θ̂(t) = θ(t) - Δθ·e^(-ζ·ωn·t)·[cos(ωd·t) + (ζ/√(1-ζ²))·sin(ωd·t)]
|
其中:
1
| ωd = ωn·√(1-ζ²) (阻尼振荡频率)
|
3.4.2 响应特性
| 阻尼比ζ |
响应特性 |
超调量 |
调节时间 |
| ζ < 0.7 |
欠阻尼 |
有超调 |
短 |
| ζ = 0.707 |
临界阻尼 |
4.3% |
最短 |
| ζ > 1 |
过阻尼 |
无 |
长 |
推荐:ζ = 0.707~1.0
3.5 PLL调试技巧
3.5.1 参数调节方法
方法1:经验公式
1 2
| Ki = ωn² = (200~500)² = 40000~250000 Kp = 2·ζ·√Ki = 1.414·√Ki
|
方法2:逐步调试
1 2 3 4 5
| 1. 设置Ki = 0,只调节Kp 2. 增大Kp直到速度开始振荡 3. Kp取振荡值的50% 4. 增加Ki消除稳态误差 5. 微调Kp和Ki
|
3.5.2 常见问题诊断
| 现象 |
原因 |
解决方案 |
| 角度跟踪慢 |
Kp太小 |
增大Kp |
| 角度振荡 |
Kp太大 |
减小Kp |
| 稳态误差大 |
Ki太小 |
增大Ki |
| 速度估计噪声大 |
需要滤波 |
增加速度滤波 |
| 负载变化丢步 |
动态响应慢 |
增大Kp和Ki |
3.6 PLL完整信号流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| ┌────────────────────────────────────────────────────────────────┐ │ PLL完整信号流图 │ └────────────────────────────────────────────────────────────────┘
Eα_flt = -Ke·ω·sin(θ) Eβ_flt = Ke·ω·cos(θ) │ │ └──────────┬───────────────────┘ ↓ ┌──────────────────────┐ │ 相位误差计算 │ │ θe = -Eα·cos(θ̂) │ │ -Eβ·sin(θ̂) │ │ = Ke·ω·sin(θ-θ̂) │ └──────────────────────┘ │ ↓ θe ┌──────────────────────┐ │ PI控制器 │ │ ┌─────────────────┐ │ │ │ P项: Kp·θe │ │ │ └─────────────────┘ │ │ ┌─────────────────┐ │ │ │ I项: ∫Ki·θe·dt │ │ │ └─────────────────┘ │ └──────────────────────┘ │ ↓ ω̂ (电角速度 rad/s) ┌──────────────────────┐ │ 速度低通滤波(可选) │ │ ω̂_f = k·ω̂_f + (1-k)·ω̂│ └──────────────────────┘ │ ↓ ┌──────────────────────┐ │ 积分器 │ │ θ̂(k+1) = θ̂(k) + ω̂·Ts │ └──────────────────────┘ │ ↓ θ̂ (电角度 rad) ┌──────────────────────┐ │ 角度归一化 │ │ θ̂ = θ̂ % (2·π) │ └──────────────────────┘ │ ↓ ┌──────────────────────┐ │ 机械角度转换 │ │ θ_mech = θ̂ / PP │ └──────────────────────┘ │ └──→ 反馈到相位误差计算
|
4. SVPWM深度解析
4.1 SVPWM基本原理
4.1.1 什么是SVPWM?
SVPWM(Space Vector Pulse Width Modulation,空间矢量脉宽调制)是一种:
- 利用三相电压空间矢量
- 在复平面内合成任意方向电压矢量
- 实现电机高效控制的方法
4.1.2 为什么用SVPWM?
| 方法 |
电压利用率 |
谐波 |
实现复杂度 |
| SPWM(正弦PWM) |
0.866 |
较大 |
简单 |
| SVPWM |
1.0 |
小 |
中等 |
4.2 三相逆变器基本原理
4.2.1 逆变器拓扑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Vdc │ ┌─────┴─────┐ │ │ ┌─┴─┐ ┌─┴─┐ ┌─┴─┐ │ S1│ │ S3│ │ S5│ └─┬─┘ └─┬─┘ └─┬─┘ │ A相 │ B相 │ C相 ┌─┴─┐ ┌─┴─┐ ┌─┴─┐ │ S2│ │ S4│ │ S6│ └─┬─┘ └─┬─┘ └─┬─┘ │ │ │ └─────┬─────┴─────┬─────┘ │ │ GND 电机
|
4.2.2 开关状态
每相有2种状态:上管导通(1)或下管导通(0)
三相共有 2³ = 8 种开关状态:
| 状态 |
S1 S3 S5 |
A B C |
电压矢量 |
类型 |
| 0 |
0 0 0 |
0 0 0 |
零矢量 |
零矢量 |
| 1 |
1 0 0 |
Vdc 0 0 |
U1 |
基本矢量 |
| 2 |
1 1 0 |
Vdc Vdc 0 |
U2 |
基本矢量 |
| 3 |
0 1 0 |
0 Vdc 0 |
U3 |
基本矢量 |
| 4 |
0 1 1 |
0 Vdc Vdc |
U4 |
基本矢量 |
| 5 |
0 0 1 |
0 0 Vdc |
U5 |
基本矢量 |
| 6 |
1 0 1 |
Vdc 0 Vdc |
U6 |
基本矢量 |
| 7 |
1 1 1 |
Vdc Vdc Vdc |
零矢量 |
零矢量 |
4.3 空间矢量图
4.3.1 α-β坐标系下的基本矢量
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| β轴 ↑ │ U3(010) │ U2(110) \ │ / \ │ / \ │ / \ │ / ┼────────→ α轴 / │ \ / │ \ / │ \ U4(011)/ │U1(100) │
|
六个基本矢量将平面分为6个扇区:
1 2 3 4 5 6 7 8
| 扇区3 ┌───────────┐ │ │ 扇区2│ │ │扇区4 │ ● │ │ │ └───────────┘ 扇区6 扇区5 扇区1
|
4.3.2 基本矢量坐标
| 矢量 |
开关状态 |
Uα |
Uβ |
幅值 |
| U0 |
000 |
0 |
0 |
0 |
| U1 |
100 |
2/3·Vdc |
0 |
2/3·Vdc |
| U2 |
110 |
1/3·Vdc |
√3/3·Vdc |
2/3·Vdc |
| U3 |
010 |
-1/3·Vdc |
√3/3·Vdc |
2/3·Vdc |
| U4 |
011 |
-2/3·Vdc |
0 |
2/3·Vdc |
| U5 |
001 |
-1/3·Vdc |
-√3/3·Vdc |
2/3·Vdc |
| U6 |
101 |
1/3·Vdc |
-√3/3·Vdc |
2/3·Vdc |
| U7 |
111 |
0 |
0 |
0 |
重要结论:所有基本矢量幅值相等 = 2/3·Vdc
4.4 SVPWM算法推导
4.4.1 目标
给定任意目标电压矢量 Uref,求:
- 所在扇区
- 相邻两个基本矢量的作用时间
- 零矢量作用时间
4.4.2 扇区判断
计算中间变量:
1 2 3
| V1 = Uβ V2 = (√3·Uα - Uβ) / 2 V3 = (-√3·Uα - Uβ) / 2
|
扇区判断:
1
| sector = 1 + sign(V1) + 2·sign(V2) + 4·sign(V3)
|
其中:
1 2
| sign(x) = { 1, x ≥ 0 { 0, x < 0
|
简化判断(使用角度):
1
| sector = floor(angle / (π/3)) + 1;
|
4.4.3 作用时间计算
在扇区I中,目标矢量由U1和U2合成:
1
| Uref = (T1/Tpwm)·U1 + (T2/Tpwm)·U2 + (T0/Tpwm)·U0
|
约束条件:
在α-β坐标系分解:
1 2
| Uα·Tpwm = T1·|U1| + T2·|U2|·cos(60°) Uβ·Tpwm = T2·|U2|·sin(60°)
|
解得:
1 2 3
| T1 = √3·Tpwm·(Uα/Vdc - Uβ/(√3·Vdc)) T2 = 2·Tpwm·(Uβ/(√3·Vdc)) T0 = Tpwm - T1 - T2
|
通用公式(适用于所有扇区):
定义归一化时间:
1 2
| T1 = √3·sin(θk·π/3 - θ)·(|Uref|/Vdc)·Tpwm T2 = √3·sin(θ - (θk-1)·π/3)·(|Uref|/Vdc)·Tpwm
|
其中:
- θk:扇区结束角度
- θ:目标电压角度
- |Uref|:目标电压幅值
4.5 七段式SVPWM
4.5.1 为什么用七段式?
目标:减少开关损耗,使每次只切换一相
4.5.2 扇区I的七段式
1 2 3 4 5 6 7 8
| 时间轴: 0 T0/4 T0/4+T1/2 T0/4+T1/2+T2 Tpwm │ │ │ │ │ │ │ │ │ │ 状态: U0 U1 U2 U7 U0 │ │ │ │ │ 000 100 110 111 000
开关次数: 0 1 2 3 4
|
各相状态:
1 2 3 4
| U0(000) U1(100) U2(110) U7(111) A: 0 → 1 → 1 → 1 → 0 B: 0 → 0 → 1 → 1 → 0 C: 0 → 0 → 0 → 1 → 0
|
4.5.3 扇区I的占空比计算
1 2 3
| Ta = T1 + T2 + T0/4 Tb = T2 + T0/4 Tc = T0/4
|
归一化到[0,1]:
1 2 3
| Da = Ta / Tpwm Db = Tb / Tpwm Dc = Tc / Tpwm
|
4.5.6 各扇区占空比表
| 扇区 |
Ta |
Tb |
Tc |
| I(0°~60°) |
T1+T2+T0/2 |
T2+T0/2 |
T0/2 |
| II(60°~120°) |
T1+T0/2 |
T1+T2+T0/2 |
T0/2 |
| III(120°~180°) |
T0/2 |
T1+T2+T0/2 |
T2+T0/2 |
| IV(180°~240°) |
T0/2 |
T1+T0/2 |
T1+T2+T0/2 |
| V(240°~300°) |
T2+T0/2 |
T0/2 |
T1+T2+T0/2 |
| VI(300°~360°) |
T1+T2+T0/2 |
T0/2 |
T1+T0/2 |
4.6 SVPWM代码实现
4.6.1 完整代码(带详细注释)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
| void M0_setTorque(float Uq, float Ud, float angle_el) { Cur_vol_M0.Uq = Uq; Cur_vol_M0.Ud = Ud;
float Uout; float voltage_angle;
if (Ud != 0) { Uout = _sqrt(Ud * Ud + Uq * Uq) / voltage_power_supply;
voltage_angle = _normalizeAngle(angle_el + atan2(Uq, Ud)); } else { Uout = Uq / voltage_power_supply;
voltage_angle = _normalizeAngle(angle_el + _PI_2); }
int sector = floor(voltage_angle / _PI_3) + 1;
float T1 = _SQRT3 * sin(sector * _PI_3 - voltage_angle) * Uout; float T2 = _SQRT3 * sin(voltage_angle - (sector - 1.0f) * _PI_3) * Uout; float T0 = 1 - T1 - T2;
float Ta, Tb, Tc;
switch (sector) { case 1: Ta = T1 + T2 + T0 / 2; Tb = T2 + T0 / 2; Tc = T0 / 2; break;
case 2: Ta = T1 + T0 / 2; Tb = T1 + T2 + T0 / 2; Tc = T0 / 2; break;
case 3: Ta = T0 / 2; Tb = T1 + T2 + T0 / 2; Tc = T2 + T0 / 2; break;
case 4: Ta = T0 / 2; Tb = T1 + T0 / 2; Tc = T1 + T2 + T0 / 2; break;
case 5: Ta = T2 + T0 / 2; Tb = T0 / 2; Tc = T1 + T2 + T0 / 2; break;
case 6: Ta = T1 + T2 + T0 / 2; Tb = T0 / 2; Tc = T1 + T0 / 2; break;
default: Ta = 0; Tb = 0; Tc = 0; }
float Ua = Ta * voltage_power_supply; float Ub = Tb * voltage_power_supply; float Uc = Tc * voltage_power_supply;
M0_setPwm(Ua, Ub, Uc); }
|
4.6.2 PWM输出函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| void M0_setPwm(float Ua, float Ub, float Uc) {
Ua = _constrain(Ua, 0.0f, voltage_power_supply); Ub = _constrain(Ub, 0.0f, voltage_power_supply); Uc = _constrain(Uc, 0.0f, voltage_power_supply);
float dc_a = _constrain(Ua / voltage_power_supply, 0.0f, 1.0f); float dc_b = _constrain(Ub / voltage_power_supply, 0.0f, 1.0f); float dc_c = _constrain(Uc / voltage_power_supply, 0.0f, 1.0f);
ledcWrite(0, dc_a * 255); ledcWrite(1, dc_b * 255); ledcWrite(2, dc_c * 255); }
|
4.7 SVPWM详细图解
4.7.1 电压矢量合成(扇区I为例)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| β轴 ↑ │ │ │ U3(010) │ U2(110) \ │ / \ │ / \ │ / \ │ / U4───────┼─────────U1(100) (011) / │ \ / │ \ / │ \ / │ \ U5(001)/ │ \ U6(101) │ │ ◉─────────────┼─────────────→ α轴 Uref │ │
目标矢量Uref在扇区I,由U1和U2合成:
Uref = (T1/Tpwm)·U1 + (T2/Tpwm)·U2
|
4.7.2 七段式开关序列(扇区I)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| T0/4 T1/2 T2 T1/2 T0/4 ←─────────→←──────────→←────────────→←──────────→←─────────→ ┌──────────┬────────────┬─────────────┬───────────┬──────────┐ │ │ │ │ │ │ │ U0 │ U1 │ U2 │ U7 │ U0 │ │ (000) │ (100) │ (110) │ (111) │ (000) │ │ │ │ │ │ │ └──────────┴────────────┴─────────────┴───────────┴──────────┘
A相: 0 ──────── 1 ─────────── 1 ──────────── 1 ───────── 0 B相: 0 ──────── 0 ─────────── 1 ──────────── 1 ───────── 0 C相: 0 ──────── 0 ─────────── 0 ──────────── 1 ───────── 0
开关次数: 1次 1次 1次 1次
总开关次数: 4次(最小)
|
4.8 SVPWM与SPWM对比
4.8.1 电压利用率
SPWM:
1 2
| 最大相电压幅值 = Vdc/2 最大线电压幅值 = √3·Vdc/2 ≈ 0.866·Vdc
|
SVPWM:
1 2
| 最大相电压幅值 = Vdc/√3 最大线电压幅值 = Vdc
|
结论:SVPWM电压利用率高约15.5%
4.8.2 谐波特性
SPWM:谐波集中在载波频率附近
SVPWM:谐波分布更均匀,幅值更小
4.8.3 实现复杂度
| 方法 |
计算量 |
存储需求 |
实时性 |
| SPWM |
小 |
小 |
好 |
| SVPWM |
中 |
中 |
中 |
5. 代码架构分析
5.1 文件结构
1 2 3 4 5 6 7 8
| DengFOC_SMO_full_control/ ├── DengFOC_SMO_full_control.ino # 主程序入口 ├── DengFOC.h/cpp # FOC核心控制 ├── SMO.h/cpp # ⭐滑模观测器 ├── InlineCurrent.h/cpp # 电流采样 ├── pid.h/cpp # PID控制器 ├── lowpass_filter.h/cpp # 低通滤波 └── AS5600.h/cpp # 编码器(调试用)
|
5.2 主程序流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void loop() { runFOC();
if (VF_start(0, CCW) == 1) { DFOC_M0_SMO_CUR_setSpeed(serial_motor_target()); }
serialReceiveUserCommand(); }
|
5.3 runFOC核心循环详解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void runFOC() { CS_M0.getPhaseCurrents(); CS_M1.getPhaseCurrents();
if (ctrl_mode == SENSELESS) {
Cur_vol_M0 = cal_Iq_Id(CS_M0.current_a, CS_M0.current_b, SMO_M0.Est_Theta);
SMO_M0.SMO_closeloop(Cur_vol_M0);
} else { Cur_vol_M0 = cal_Iq_Id(CS_M0.current_a, CS_M0.current_b, S0_electricalAngle()); } }
|
6. 核心模块详解
6.1 坐标变换模块
Clarke变换(ABC → αβ)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Cur_vol_s cal_Iq_Id(float current_a, float current_b, float angle_el) { Cur_vol_s r_Cur_vol;
r_Cur_vol.I_alpha = current_a; r_Cur_vol.I_beta = _1_SQRT3 * current_a + _2_SQRT3 * current_b;
float ct = cos(angle_el); float st = sin(angle_el); r_Cur_vol.I_d = r_Cur_vol.I_alpha * ct + r_Cur_vol.I_beta * st; r_Cur_vol.I_q = r_Cur_vol.I_beta * ct - r_Cur_vol.I_alpha * st;
return r_Cur_vol; }
|
6.2 电流采样模块
1 2 3 4 5 6 7 8 9 10
| void CurrSense::getPhaseCurrents() { float voltage_a = readADCVoltageInline(pinA); float voltage_b = readADCVoltageInline(pinB);
current_a = (voltage_a - offset_ia) * gain_a; current_b = (voltage_b - offset_ib) * gain_b; }
|
6.3 PID控制模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| float PIDController::operator()(float error) { float Ts = (timestamp_now - timestamp_prev) * 1e-6f;
float proportional = P * error;
float integral = integral_prev + I * Ts * 0.5f * (error + error_prev); integral = _constrain(integral, -limit, limit);
float derivative = D * (error - error_prev) / Ts;
float output = proportional + integral + derivative; output = _constrain(output, -limit, limit);
return output; }
|
7. 调参指南
7.1 电机参数测量
| 参数 |
测量方法 |
典型值(2208) |
| Rs |
万用表测两相/2 |
8.25Ω |
| Ls |
电桥表测两相/2 |
4.256mH |
| 极对数 |
查规格书或实测 |
7 |
7.2 SMO参数调试
滑模增益h
1 2 3 4 5
| 现象:电机不转 → 增大h(×1.5)
现象:高频噪音 → 减小h(×0.8)
|
PLL参数
1 2 3 4 5 6 7
| // 经验公式 Ki = ωn² (ωn = 200~500) Kp = 2·ζ·√Ki (ζ = 0.707)
// 示例 Ki = 40000 Kp = 521
|
7.3 完整调参流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 1. 硬件检查 ├─ 确认相序 ├─ 确认电流采样方向 └─ 确认PWM输出
2. 有感测试 ├─ 切换到SENSE模式 ├─ 校准编码器 └─ 验证电机能正常转动
3. SMO参数调试 ├─ 设置h=0.3 ├─ 设置PLL_kp=521, PLL_ki=40000 ├─ 调试h直到稳定 └─ 微调PLL参数
4. 电流环调试 ├─ P=1~3, I=100~300 └─ 增大P到震荡点×0.5
5. 速度环调试 ├─ P=0.02, I=0.5 └─ 根据响应调整
6. VF启动调试 ├─ 调整加速度VF_acc ├─ 调整最大速度VF_max_vel └─ 调整电压增量VF_uq_delta
|
8. 常见问题与调试
8.1 问题诊断表
| 现象 |
可能原因 |
排查方法 |
| 电机不转 |
h太小 |
增大h到1.0测试 |
| 抖动严重 |
PLL参数 |
减小PLL_kp |
| 只能单向 |
_dir错误 |
检查方向设置 |
| 启动失败 |
VF参数 |
减小VF_acc |
| 角度漂移 |
滤波太强 |
减小滤波系数 |
8.2 调试技巧
串口监控
1 2 3
| Serial.printf("Est_Theta=%.2f, Real_Theta=%.2f, Iq=%.2f, speed=%.2f\n", SMO_M0.Est_Theta, S0_electricalAngle(), DFOC_M0_Current(), SMO_M0.Est_speed);
|
使用串口绘图器
Arduino IDE → 工具 → 串口绘图器
附录A:重要公式汇总
A.1 电机模型
1 2 3 4 5
| Uα = Rs·Iα + Ls·(dIα/dt) + Eα Uβ = Rs·Iβ + Ls·(dIβ/dt) + Eβ
Eα = -Ke·ω·sin(θ) Eβ = Ke·ω·cos(θ)
|
A.2 滑模观测器
1 2 3 4 5 6 7 8
| dÎα/dt = -(Rs/Ls)·Îα + (1/Ls)·(Uα - Êα) dÎβ/dt = -(Rs/Ls)·Îβ + (1/Ls)·(Uβ - Êβ)
sα = Îα - Iα sβ = Îβ - Iβ
Êα = h·sat(sα) Êβ = h·sat(sβ)
|
A.3 PLL
1 2 3 4 5 6
| θe = -Eα·cos(θ̂) - Eβ·sin(θ̂) ω̂ = Kp·θe + Ki·∫θe·dt θ̂ = ∫ω̂·dt
Ki = ωn² Kp = 2·ζ·ωn
|
A.4 SVPWM
1 2 3
| T1 = √3·sin(θk·π/3 - θ)·(|Uref|/Vdc)·Tpwm T2 = √3·sin(θ - (θk-1)·π/3)·(|Uref|/Vdc)·Tpwm T0 = Tpwm - T1 - T2
|
文档版本:v2.0
最后更新:2025年
适用硬件:DengFOC V4
原作者:灯哥开源
文档整理:深度学习版