DengFOC开环无刷电机控制代码完整解读
这是灯哥开源的纯手写ESP32无刷电机开环FOC控制代码,不依赖任何第三方FOC库,仅使用ESP32原生LED PWM外设实现三相PWM输出,是学习FOC原理的绝佳入门代码。
一、完整IO配置清单(核心问题)
代码中总共使用了4个GPIO引脚,全部配置为输出模式,具体如下:
| 引脚号 | 功能定义 | 工作模式 | 详细配置 |
|---|---|---|---|
| GPIO12 | 电机驱动使能引脚 | 数字输出 | 初始电平:HIGH(高电平使能V4版本电机驱动板) |
| GPIO32 | PWM A相输出 | LED PWM输出 | 绑定通道0,频率30kHz,8位分辨率(0-255) |
| GPIO33 | PWM B相输出 | LED PWM输出 | 绑定通道1,频率30kHz,8位分辨率(0-255) |
| GPIO25 | PWM C相输出 | LED PWM输出 | 绑定通道2,频率30kHz,8位分辨率(0-255) |
IO配置关键说明
- 30kHz PWM频率:选择这个频率是为了完全避开人耳听觉范围(20Hz-20kHz),消除电机运行时的高频啸叫声
- 8位分辨率:对应占空比范围0-255,对于开环FOC控制已经足够,同时能保证30kHz的高频率输出
- GPIO12使能逻辑:DengFOC V4驱动板采用高电平使能,低电平会切断电机电源
- ESP32 LED PWM特性:最多支持16个独立PWM通道,可任意映射到GPIO引脚,非常适合多相电机控制
二、代码整体结构与核心逻辑
1. 全局变量与宏定义
// 三相PWM输出引脚定义intpwmA=32;intpwmB=33;intpwmC=25;// 数值约束宏,替代Arduino原生constrain函数#define_constrain(amt,low,high)((amt)<(low)?(low):((amt)>(high)?(high):(amt)))// 系统参数floatvoltage_power_supply=12.6;// 电源电压(3S锂电池满电电压)floatshaft_angle=0;// 电机机械角度(开环积分得到)floatopen_loop_timestamp=0;// 上一次循环的时间戳floatzero_electric_angle=0;// 电角度零点偏移(开环时无需校准)2. 初始化函数setup()
voidsetup(){Serial.begin(115200);// 串口初始化,用于调试输出// 电机驱动使能pinMode(12,OUTPUT);digitalWrite(12,HIGH);// 开启电机驱动电源// PWM引脚模式设置pinMode(pwmA,OUTPUT);pinMode(pwmB,OUTPUT);pinMode(pwmC,OUTPUT);// LED PWM控制器配置ledcSetup(0,30000,8);// 通道0,30kHz,8位分辨率ledcSetup(1,30000,8);// 通道1,30kHz,8位分辨率ledcSetup(2,30000,8);// 通道2,30kHz,8位分辨率// 将PWM引脚绑定到对应的LED PWM通道ledcAttachPin(pwmA,0);ledcAttachPin(pwmB,1);ledcAttachPin(pwmC,2);Serial.println("完成PWM初始化设置");delay(3000);// 3秒延时,方便观察初始化完成}3. 核心数学辅助函数
// 机械角度转换为电角度// 电角度 = 机械角度 × 极对数float_electricalAngle(floatshaft_angle,intpole_pairs){return(shaft_angle*pole_pairs);}// 将任意角度归一化到 [0, 2π] 范围// 解决角度溢出和负数问题float_normalizeAngle(floatangle){floata=fmod(angle,2*PI);returna>=0?a:(a+2*PI);}4. PWM输出设置函数
voidsetPwm(floatUa,floatUb,floatUc){// 将三相电压转换为占空比(0-1)// 占空比 = 相电压 / 电源电压dc_a=_constrain(Ua/voltage_power_supply,0.0f,1.0f);dc_b=_constrain(Ub/voltage_power_supply,0.0f,1.0f);dc_c=_constrain(Uc/voltage_power_supply,0.0f,1.0f);// 写入PWM寄存器(8位分辨率对应0-255)ledcWrite(0,dc_a*255);ledcWrite(1,dc_b*255);ledcWrite(2,dc_c*255);}5. FOC核心变换函数
voidsetPhaseVoltage(floatUq,floatUd,floatangle_el){// 电角度归一化,加上零点偏移angle_el=_normalizeAngle(angle_el+zero_electric_angle);// 帕克逆变换(d-q坐标系 → α-β坐标系)// 开环控制时Ud=0,只需要Uq分量Ualpha=-Uq*sin(angle_el);Ubeta=Uq*cos(angle_el);// 克拉克逆变换(α-β坐标系 → 三相静止坐标系)// 这里使用了中心对齐PWM的简化计算,加上电源电压的一半作为直流偏置Ua=Ualpha+voltage_power_supply/2;Ub=(sqrt(3)*Ubeta-Ualpha)/2+voltage_power_supply/2;Uc=(-Ualpha-sqrt(3)*Ubeta)/2+voltage_power_supply/2;// 设置三相PWM输出setPwm(Ua,Ub,Uc);}6. 开环速度控制函数
floatvelocityOpenloop(floattarget_velocity){// 获取当前系统时间(微秒)unsignedlongnow_us=micros();// 计算两次循环之间的时间间隔(秒)floatTs=(now_us-open_loop_timestamp)*1e-6f;// 时间戳溢出保护(micros()约70分钟溢出一次)if(Ts<=0||Ts>0.5f)Ts=1e-3f;// 开环角度积分:通过目标速度和时间间隔计算当前机械角度// 这是开环控制的核心,没有编码器反馈,纯靠时间积分估算角度shaft_angle=_normalizeAngle(shaft_angle+target_velocity*Ts);// 设置q轴电压(决定输出力矩)// 最大安全值为 voltage_power_supply/2,超过会导致电压限幅失真floatUq=voltage_power_supply/3;// 计算并输出三相电压// 极对数为7,对应DengFOC配套的2208电机setPhaseVoltage(Uq,0,_electricalAngle(shaft_angle,7));// 更新时间戳open_loop_timestamp=now_us;returnUq;}7. 主循环函数
voidloop(){// 以5rad/s的角速度开环运行// 1rad/s ≈ 9.55rpm,5rad/s ≈ 47.75rpm(机械转速)velocityOpenloop(5);}三、代码关键特性与注意事项
- 纯手写无库依赖:代码不依赖SimpleFOC等任何第三方库,所有FOC算法都由基础数学运算实现,非常适合学习原理
- 开环控制特性:
- 不需要编码器或霍尔传感器
- 低速力矩较小,高速容易丢步
- 适合入门学习和简单应用
- 可修改参数:
voltage_power_supply:根据实际电源电压修改(如12V电源改为12.0)pole_pairs:根据电机极对数修改(代码中为7,对应2208电机)Uq:调整输出力矩,最大不超过voltage_power_supply/2target_velocity:修改电机目标转速(单位:rad/s)
- 硬件兼容性:仅在DengFOC V4开发板上测试过,其他ESP32开发板需要根据实际引脚定义修改
需要我帮你把这段代码改成支持正反转切换和串口调速的版本吗?