ESP32 无感FOC - SMO滑模观测器代码解析文档

项目概述

本项目是基于ESP32的无感FOC(Field Oriented Control,磁场定向控制)电机控制实现,使用滑模观测器(Sliding Mode Observer, SMO)进行转子位置和速度估算。项目支持双电机控制,采用电流闭环控制方式。

硬件平台

  • MCU: ESP32
  • 电流采样: 两相采样(A、B相)
  • 位置传感器: AS5600磁编码器(仅用于校准,运行时不使用)
  • 功率级: 三相桥驱动

核心特性

  • 滑模观测器(SMO)无感位置估算
  • 电流闭环FOC控制
  • 双电机支持(M0和M1)
  • 可配置的PID控制器
  • 低通滤波器

文件结构

1
2
3
4
5
6
7
8
4_SMO滑模观测器/
├── 4_SMO滑模观测器.ino # 主程序入口
├── DengFOC.h/cpp # FOC核心控制库
├── InlineCurrent.h/cpp # 电流采样模块
├── AS5600.h/cpp # AS5600编码器驱动(校准用)
├── lowpass_filter.h/cpp # 低通滤波器
├── pid.h/cpp # PID控制器
└── esp32_adc_driver.h/cpp # ESP32 ADC底层驱动

一、主程序 (4_SMO滑模观测器.ino) 详解

1.1 全局变量定义

1
2
3
4
5
// SMO相关变量
float Rs = 8.25; // 定子电阻(欧姆)
float Ls = 0.004256; // 定子电感(亨利)
float h = 0.3; // 滑模增益
float SMO_Ualpha, SMO_Ubeta; // SMO输入电压(αβ坐标系)

说明

  • Rs, Ls: 电机参数,需要根据实际电机调整
  • h: 滑模增益,影响SMO收敛速度和稳定性
1
2
3
4
5
6
7
// SMO估算变量
float Est_Ialpha, Est_Ibeta; // 估算电流(αβ坐标系)
float Ealpha, Ebeta; // 反电动势估算值
float Ealpha_flt, Ebeta_flt; // 滤波后的反电动势
float SMO_Est_theta; // 估算的电角度
float SMO_Vel; // 估算的转速
float SMO_Theta; // 估算的累积角度

1.2 setup()函数 - 初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void setup() {
Serial.begin(115200);
DFOC_enable(); // 使能电机驱动
DFOC_Vbus(12); // 设置供电电压12V

// 传感器校准(找到电角度零点)
DFOC_M0_alignSensor(7, 1); // M0: 7极对,方向1
DFOC_M1_alignSensor(7, -1); // M1: 7极对,方向-1

// PID参数设置
DFOC_M0_SET_CURRENT_PID(3, 200, 0, 100000); // 电流环
DFOC_M1_SET_CURRENT_PID(3, 200, 0, 100000);
DFOC_M0_SET_VEL_PID(0.02, 0.1, 0, 100000, 6); // 速度环
DFOC_M1_SET_VEL_PID(0.02, 0.5, 0, 100000, 6);
}

1.3 loop()函数 - 主循环

1
2
3
4
5
void loop() {
runFOC(); // 运行FOC算法
DFOC_M0_setVelocity(80); // 设置目标速度80 rad/s
serialReceiveUserCommand(); // 接收串口命令
}

二、SMO滑模观测器核心算法

2.1 SMO原理简介

滑模观测器是一种基于变结构控制理论的状态观测器,其核心思想是:

  1. 根据电机电压方程构建电流观测器
  2. 通过滑模控制律(符号函数)使观测电流收敛到实际电流
  3. 从滑模控制信号中提取反电动势信息
  4. 由反电动势计算转子位置

2.2 核心函数:SMO_position_estimate()

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
void SMO_position_estimate(){
// 1. 计算采样时间
uint32_t now_time = micros();
Ts = (now_time - SMO_last_time) * 1e-6f;
SMO_last_time = now_time;
if (Ts < 0 || Ts > 5e-3f) Ts = 1e-3f;

// 2. 读取实际电流并转换到αβ坐标系
float Ialpha = CS_M0.current_a;
float Ibeta = _1_SQRT3 * CS_M0.current_a + _2_SQRT3 * CS_M0.current_b;

// 3. 电流观测器更新(欧拉法离散化)
// PMSM电压方程: dI/dt = (-Rs*I + U - E) / Ls
Est_Ialpha += Ts * (-Rs / Ls * Est_Ialpha + 1 / Ls * (SMO_Ualpha - Ealpha));
Est_Ibeta += Ts * (-Rs / Ls * Est_Ibeta + 1 / Ls * (SMO_Ubeta - Ebeta));

// 4. 计算电流误差
float Ialpha_Err = Est_Ialpha - Ialpha;
float Ibeta_Err = Est_Ibeta - Ibeta;

// 5. 滑模控制律(带饱和函数)
Ealpha = h * sat(Ialpha_Err, 0.5f);
Ebeta = h * sat(Ibeta_Err, 0.5f);

// 6. 反电动势滤波(减少抖动)
Ealpha_flt = 0.1 * Ealpha_flt + 0.9 * Ealpha;
Ebeta_flt = 0.1 * Ebeta_flt + 0.9 * Ebeta;

// 7. 计算电角度:θ = -atan(Eα/Eβ)
SMO_Est_theta = (-atan(Ealpha_flt / Ebeta_flt));
SMO_Est_theta = _normalizeAngle(SMO_Est_theta);
}

2.3 饱和函数 sat()

1
2
3
4
5
float sat(float err, float limits) {
if (err > limits) return 1; // 上饱和
else if (err < -limits) return -1; // 下饱和
else return err / limits; // 线性区
}

作用:减少滑模控制的抖振现象(Chattering)

2.4 SMO速度计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
float getSMOVel(){
float Ts = (SMO_angle_prev_ts - SMO_vel_angle_prev_ts)*1e-6;
if(Ts <= 0) Ts = 1e-3f;

// 计算角速度
float vel = ((float)(SMO_full_rotations - SMO_vel_full_rotations)*_2PI
+ (SMO_angle_prev - SMO_vel_angle_prev)) / Ts;
vel = vel/14; // 除以极对数,得到机械角速度

// 更新历史值
SMO_vel_angle_prev = SMO_angle_prev;
SMO_vel_full_rotations = SMO_full_rotations;
SMO_vel_angle_prev_ts = SMO_angle_prev_ts;
return vel;
}

三、FOC核心控制 (DengFOC.cpp)

3.1 坐标变换

3.1.1 帕克逆变换(dq → αβ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void M0_setTorque(float Uq, float angle_el) {
Uq = _constrain(Uq, -(voltage_power_supply)/2, (voltage_power_supply)/2);
float Ud = 0; // d轴电压设为0(最大转矩/电流比控制)
angle_el = _normalizeAngle(angle_el);

// 帕克逆变换
float Ualpha = -Uq*sin(angle_el);
float Ubeta = Uq*cos(angle_el);

// 保存给SMO使用
SMO_Ualpha = Ualpha;
SMO_Ubeta = Ubeta;

// 克拉克逆变换(αβ → ABC)
float Ua = Ualpha + voltage_power_supply/2;
float Ub = (sqrt(3)*Ubeta-Ualpha)/2 + voltage_power_supply/2;
float Uc = (-Ualpha-sqrt(3)*Ubeta)/2 + voltage_power_supply/2;

M0_setPwm(Ua, Ub, Uc);
}

3.1.2 克拉克变换(ABC → αβ)

1
2
3
4
5
6
7
8
9
float cal_Iq_Id(float current_a, float current_b, float angle_el) {
float I_alpha = current_a;
float I_beta = _1_SQRT3 * current_a + _2_SQRT3 * current_b;

float ct = cos(angle_el);
float st = sin(angle_el);
float I_q = I_beta * ct - I_alpha * st; // 帕克变换
return I_q;
}

3.2 闭环控制函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 电流闭环
void DFOC_M0_setTorque(float Target) {
M0_setTorque(current_loop_M0(Target - DFOC_M0_Current()), S0_electricalAngle());
}

// 速度闭环
void DFOC_M0_setVelocity(float Target) {
DFOC_M0_setTorque(DFOC_M0_VEL_PID((Target - DFOC_M0_Velocity())*180/PI));
}

// 位置-速度闭环(串级控制)
void DFOC_M0_set_Velocity_Angle(float Target) {
DFOC_M0_setTorque(DFOC_M0_VEL_PID(
DFOC_M0_ANGLE_PID((Target - DFOC_M0_Angle())*180/PI) - DFOC_M0_Velocity()
));
}

四、电流采样模块 (InlineCurrent.cpp)

4.1 硬件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CurrSense::CurrSense(int Mot_Num) {
if(Mot_Num == 0) {
pinA = 39; // M0 A相ADC
pinB = 36; // M0 B相ADC
_shunt_resistor = 0.01; // 10mΩ分流电阻
amp_gain = 50; // 运放增益50倍

// 计算电压-电流转换系数
volts_to_amps_ratio = 1.0f / _shunt_resistor / amp_gain;
gain_a = volts_to_amps_ratio;
gain_b = volts_to_amps_ratio;
}
// M1类似...
}

4.2 ADC零点校准

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void CurrSense::calibrateOffsets() {
const int calibration_rounds = 1000;
offset_ia = 0;
offset_ib = 0;

// 采样1000次求平均
for (int i = 0; i < calibration_rounds; i++) {
offset_ia += readADCVoltageInline(pinA);
offset_ib += readADCVoltageInline(pinB);
delay(1);
}
offset_ia /= calibration_rounds;
offset_ib /= calibration_rounds;
}

4.3 读取三相电流

1
2
3
4
5
6
void CurrSense::getPhaseCurrents() {
current_a = (readADCVoltageInline(pinA) - offset_ia) * gain_a;
current_b = (readADCVoltageInline(pinB) - offset_ib) * gain_b;
current_c = (!_isset(pinC)) ? 0 :
(readADCVoltageInline(pinC) - offset_ic) * gain_c;
}

五、PID控制器 (pid.cpp)

5.1 PID算法实现

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
float PIDController::operator()(float error) {
unsigned long timestamp_now = micros();
float Ts = (timestamp_now - timestamp_prev) * 1e-6f;
if(Ts <= 0 || Ts > 0.5f) Ts = 1e-3f;

// P环
float proportional = P * error;

// I环(Tustin积分法)
float integral = integral_prev + I*Ts*0.5f*(error + error_prev);
integral = _constrain(integral, -limit, limit);

// D环
float derivative = D*(error - error_prev)/Ts;

// PID输出
float output = proportional + integral + derivative;
output = _constrain(output, -limit, limit);

// 输出变化率限制
if(output_ramp > 0) {
float output_rate = (output - output_prev)/Ts;
if (output_rate > output_ramp)
output = output_prev + output_ramp*Ts;
else if (output_rate < -output_ramp)
output = output_prev - output_ramp*Ts;
}

// 保存状态
integral_prev = integral;
output_prev = output;
error_prev = error;
timestamp_prev = timestamp_now;

return output;
}

5.2 PID参数说明

参数 含义 推荐范围 作用
P 比例增益 0.01~10 快速响应误差
I 积分增益 0~500 消除稳态误差
D 微分增益 0~1 抑制振荡
ramp 变化率限制 1000~100000 平滑输出
limit 输出限幅 根据母线电压 保护硬件

六、低通滤波器 (lowpass_filter.cpp)

6.1 一阶低通滤波器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
float LowPassFilter::operator()(float x) {
unsigned long timestamp = micros();
float dt = (timestamp - timestamp_prev)*1e-6f;

if (dt < 0.0f) dt = 1e-3f;
else if(dt > 0.3f) {
y_prev = x; // 超时重置
timestamp_prev = timestamp;
return x;
}

// 滤波器系数:α = Tf/(Tf + dt)
float alpha = Tf/(Tf + dt);
float y = alpha*y_prev + (1.0f - alpha)*x;

y_prev = y;
timestamp_prev = timestamp;
return y;
}

传递函数:H(s) = 1/(1 + s·Tf)

截止频率:fc = 1/(2π·Tf)

应用 Tf值 说明
速度滤波 0.01s 截止频率约16Hz
电流滤波 0.002s 截止频率约80Hz
SMO速度滤波 0.05s 截止频率约3Hz

七、编码器传感器 (AS5600.cpp)

7.1 AS5600读取

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
double Sensor_AS5600::getSensorAngle() {
uint8_t angle_reg_msb = 0x0C;
byte readArray[2];

// I2C通信读取角度寄存器
wire->beginTransmission(0x36); // AS5600地址
wire->write(angle_reg_msb);
wire->endTransmission(false);

wire->requestFrom(0x36, (uint8_t)2);
for (byte i=0; i < 2; i++) {
readArray[i] = wire->read();
}

// 12位角度解码
int _bit_resolution = 12;
int _bits_used_msb = 11-7;
float cpr = pow(2, _bit_resolution);
int lsb_used = _bit_resolution - _bits_used_msb;

uint8_t lsb_mask = (uint8_t)((2 << lsb_used) - 1);
uint8_t msb_mask = (uint8_t)((2 << _bits_used_msb) - 1);

uint16_t readValue = (readArray[1] & lsb_mask);
readValue += ((readArray[0] & msb_mask) << lsb_used);

return (readValue/(float)cpr) * _2PI;
}

7.2 传感器校准

1
2
3
4
5
6
7
8
9
10
11
12
13
void DFOC_M0_alignSensor(int _PP, int _DIR) {
M0_PP = _PP;
M0_DIR = _DIR;

// 施加已知电压矢量使电机对齐到d轴
M0_setTorque(3, _3PI_2);
delay(1000);

S0.Sensor_update();
S0_zero_electric_angle = S0_electricalAngle(); // 记录零点偏移

M0_setTorque(0, _3PI_2); // 释放
}

八、主控制循环 runFOC()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void runFOC() {
// 1. 更新传感器
S0.Sensor_update();
CS_M0.getPhaseCurrents(); // 读取电流
CS_M1.getPhaseCurrents();

// 2. 运行SMO估算器
SMO_position_estimate();

// 3. 更新SMO角度和速度
SMOThetaUpdate();
SMO_Vel = SMO_Vel_Flter(getSMOVel());
getSMOTheta();

// 4. 调试输出
if (cntt++ > 20) {
cntt = 0;
Serial.printf("%f,%f\n", SMO_Vel, DFOC_M0_Velocity());
}
}

九、控制框图

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
┌─────────────────────────────────────────────────────────────────┐
│ FOC控制框图 │
└─────────────────────────────────────────────────────────────────┘

目标值


[PID控制器] ────► Uq


[帕克逆变换] ────► Uα, Uβ ───► [SMO] ───► 估算θ, ω
│ │
▼ │
[克拉克逆变换] ────► Ua, Ub, Uc │
│ │
▼ │
[PWM输出] ────────► [电机] ────────────┘
│ │
▼ ▼
[电流采样] ◄──────── 实际Ia, Ib


[克拉克变换] ────► Iα, Iβ


[帕克变换] ──────► Iq

└───────► 反馈给PID

十、参数调试指南

10.1 电机参数(必须根据实际电机修改)

1
2
float Rs = 8.25;      // 定子电阻,用万用表测量相电阻
float Ls = 0.004256; // 定子电感,使用电桥表测量

10.2 SMO参数

参数 推荐值 调整说明
h(滑模增益) 0.1~1.0 太小收敛慢,太大抖动大
反电动势滤波系数 0.1~0.3 影响位置估算平滑度

10.3 PID调试步骤

  1. 先调电流环

    • P从0.1开始逐步增大
    • I设为0,观察电流响应
    • 增大I直到电流跟踪准确
  2. 再调速度环

    • 电流环稳定后
    • P从小增大,观察速度响应
    • 适当增加I消除稳态误差

10.4 常见问题

现象 可能原因 解决方法
电机不转 电流环P太小或电流采样错误 检查offset校准,增大P
转速抖动 SMO增益h不当,滤波参数不对 调整h和滤波系数
失步 速度环响应太慢 增大速度环P、I
电流过大 PID积分累积过快 降低I或增加ramp限制

十一、数学公式汇总

11.1 克拉克变换(ABC → αβ)

1
2
Iα = Ia
Iβ = (1/√3)·Ia + (2/√3)·Ib

11.2 帕克变换(αβ → dq)

1
2
Id = Iα·cosθ + Iβ·sinθ
Iq = Iβ·cosθ - Iα·sinθ

11.3 帕克逆变换(dq → αβ)

1
2
Uα = -Uq·sinθ + Ud·cosθ
Uβ = Uq·cosθ + Ud·sinθ

11.4 SMO电流观测器

1
2
3
4
5
dÎα/dt = (-Rs·Îα + Uα - Eα) / Ls
dÎβ/dt = (-Rs·Îβ + Uβ - Eβ) / Ls

Eα = h·sat(Îα - Iα, limit)
Eβ = h·sat(Îβ - Iβ, limit)

11.5 角度估算

1
θest = -atan(Eα/Eβ)

十二、参考文献

  1. 滑模观测器原理

    • “Sensorless Control of PMSM with Sliding Mode Observer”
    • PMSM无传感器控制经典方法
  2. FOC基础

    • SimpleFOC项目
    • Vesc开源项目
  3. 作者信息

    • 灯哥开源
    • 淘宝店铺:灯哥开源
    • 遵循GNU开源协议

文档生成日期:2026年3月
适用于:DengFOC V4 SMO滑模观测器例程