从零搭建轮腿机器人(1):基于STM32的FOC电流环实战与参数整定

发布时间:2026/6/10 16:52:37

从零搭建轮腿机器人(1):基于STM32的FOC电流环实战与参数整定 1. 从零开始为什么选择FOC电流环控制第一次接触无刷电机控制时我被各种控制算法搞得晕头转向。直到真正动手做轮腿机器人项目才发现FOC磁场定向控制电流环才是实现精准力矩控制的王道。简单来说电流环就是控制电机力气大小的核心环节就像我们控制汽车油门一样直接。你可能要问为什么不用更简单的六步换相或者正弦波控制我当初也这么想过。但实测下来六步换相会有明显的转矩脉动而普通正弦波控制又无法实现力矩的精准调节。FOC电流环最大的优势在于它能将复杂的三相交流电流转化为直观的直流控制量让力矩控制变得像调节旋钮一样简单。硬件选择上我推荐STM32F103C8T6这款经典芯片。它价格亲民某宝20元左右性能足够而且资料丰富。记得我第一次用这款芯片时就被它丰富的外设资源惊艳到了——3个ADC、高级定时器、CAN总线一应俱全完全满足FOC控制的需求。2. 硬件搭建电流采样电路设计实战电流采样是FOC控制的基础但也是最容易踩坑的环节。我最初尝试用普通运放做采样结果噪声大得根本没法用。后来改用专业电流采样运放INA240效果立竿见影。这里分享几个关键经验采样电阻选择建议用0.01Ω/3W的合金电阻。太大会影响效率太小信号又太弱。我试过0.005Ω的结果ADC根本采不到有效信号。运放配置INA240的增益选50倍A2版本正合适。基准电压要稳定在1.65V我用TL431搭建的基准源比直接用电阻分压稳定得多。布线要点采样电阻到运放的走线要尽可能短最好采用差分走线。我的第一个版本没注意这点导致采样信号被PWM噪声严重干扰。实际电路可以参考这个配置// 硬件相关宏定义 #define GAIN 50 // 运放增益 #define R_SHUNT 0.01 // 采样电阻值 #define V_REF 1.65 // 基准电压3. 软件实现从ADC采样到Park变换代码实现是很多初学者的噩梦我当初调试时曾经连续三天卡在Clark变换上。下面分享经过实战验证的代码框架首先是ADC采样部分。这里有个坑STM32的ADC在PWM环境下容易受到干扰。我的解决方案是使用定时器触发采样与PWM中心对齐加入滑动平均滤波我用了64点// ADC采样与电流计算 void ADC_GetCurrent(void) { HAL_ADC_Start(hadc1); HAL_ADC_Start(hadc2); uint16_t adc1_val HAL_ADC_GetValue(hadc1); uint16_t adc2_val HAL_ADC_GetValue(hadc2); // 转换为电压值 float Ua (float)adc2_val / 4096 * 3.3f; float Ub (float)adc1_val / 4096 * 3.3f; // 计算相电流 FOC.Current.A (Ua - offsetA) / R_SHUNT / GAIN; FOC.Current.B (Ub - offsetB) / R_SHUNT / GAIN; FOC.Current.C -FOC.Current.A - FOC.Current.B; // KCL定律 }然后是核心的Park变换实现。这里要特别注意变换系数的选择我推荐使用幅值不变型变换// Park变换实现 void Transform_Park(float angle) { float sin_theta arm_sin_f32(angle); float cos_theta arm_cos_f32(angle); // Clark变换幅值不变型 FOC.I_alpha FOC.Current.A; FOC.I_beta (FOC.Current.A 2*FOC.Current.B) * ONE_BY_SQRT3; // Park变换 FOC.I_d FOC.I_alpha * cos_theta FOC.I_beta * sin_theta; FOC.I_q FOC.I_beta * cos_theta - FOC.I_alpha * sin_theta; }4. SVPWM生成让电机转起来的魔法SVPWM空间矢量脉宽调制是FOC的最后一步也是最体现工程师智慧的部分。我把它理解为用数字信号画圆圈的艺术。实现时要注意扇区判断我总结出一个快速判断法只需要3次比较和1次查表uint8_t Get_Sector(float Ualpha, float Ubeta) { float U1 Ubeta; float U2 SQRT3_2 * Ualpha - 0.5f * Ubeta; float U3 -SQRT3_2 * Ualpha - 0.5f * Ubeta; uint8_t sector 0; if(U1 0) sector | 0x01; if(U2 0) sector | 0x02; if(U3 0) sector | 0x04; // 扇区映射表 static const uint8_t map[] {0,2,6,1,4,5,3}; return map[sector]; }占空比计算每个扇区的计算公式不同但可以统一为void Calc_Duty(uint8_t sector, float Tx, float Ty) { switch(sector) { case 1: // 扇区I dutyA Tx Ty Tz; dutyB Ty Tz; dutyC Tz; break; // 其他扇区类似... } }死区时间必须设置我烧过3个驱动芯片才记住这个教训。STM32的高级定时器可以直接配置死区时间// 定时器配置示例 TIM_HandleTypeDef htim1; htim1.Instance TIM1; htim1.Init.DeadTime 100; // 100ns死区时间5. PID参数整定从理论到实践的跨越PID整定是电流环调试的关键。我摸索出一套理论计算实验微调的方法理论计算基础参数// 电机参数 float R 0.5f; // 相电阻(Ω) float L 0.001f; // 相电感(H) float T 0.0001f; // 控制周期(s) float B 50.0f; // 带宽(rad/s) // 计算PID参数 float Kp L * B; float Ki R * B; float Kd 0; // 电流环通常不用微分实验调试技巧先调D轴Id此时电机不会转动使用阶跃信号测试从0.1A开始观察超调量和稳定时间这是我调试时的典型波形欠阻尼响应慢但稳定过阻尼响应快但超调大最佳点快速响应且超调5%抗饱和处理必须加积分限幅我有次忘记设置结果积分项溢出导致电机失控。// 带抗饱和的PID实现 float PID_Update(PID_t* pid, float error) { // 比例项 float output pid-Kp * error; // 积分项带限幅 pid-integral pid-Ki * error * pid-dt; pid-integral constrain(pid-integral, -pid-max_i, pid-max_i); output pid-integral; // 微分项 if(pid-dt 0) { output pid-Kd * (error - pid-last_err) / pid-dt; } pid-last_err error; return constrain(output, -pid-max_out, pid-max_out); }6. 实战调试避坑指南调试阶段我踩过无数坑这里分享几个典型案例电流采样异常现象是波形畸变严重。最后发现是运放电源电压不足将3.3V改为5V后立即改善。电机抖动原因是PWM频率太低8kHz提高到15kHz后运行平稳。但要注意频率越高开关损耗越大。零点漂移ADC采样存在偏移解决方法// 自动校准偏移 void Calibrate_Offset() { float sumA 0, sumB 0; for(int i0; i1000; i) { sumA ADC_Read(ADC_CHANNEL_1); sumB ADC_Read(ADC_CHANNEL_2); } offsetA sumA / 1000; offsetB sumB / 1000; }代码优化技巧使用查表法加速三角函数计算将Park变换改为汇编优化启用STM32的硬件浮点单元7. 进阶技巧提升性能的秘诀当基础功能实现后可以通过这些方法进一步提升性能前馈补偿根据电机模型计算预期电压大幅提升响应速度float feedforward R * target_current L * (target_current - last_current) / dt;自适应滤波根据转速自动调整滤波器参数float bandwidth BASE_BW SPEED_COEFF * fabs(speed); update_filter_parameters(bandwidth);过流保护硬件软件双重保护// 硬件比较器触发立即关断 HAL_TIM_PWM_Stop(htim1, TIM_CHANNEL_ALL); // 软件保护 if(fabs(current) MAX_CURRENT) { Emergency_Shutdown(); }参数自动整定通过频率响应自动计算PID参数void Auto_Tune() { // 注入测试信号 Inject_Test_Signal(); // 采集响应数据 Collect_Response(); // 计算频域特性 Calculate_Frequency_Response(); // 更新PID参数 Update_PID_Parameters(); }调试到这一步你的电流环应该已经能够实现静态误差1%阶跃响应时间1ms带宽500Hz这已经足够支撑大多数机器人应用了。我现在的轮腿机器人就是基于这套控制系统能够实现精准的力矩控制为后续的平衡控制打下坚实基础。

相关新闻