STM32CubeMX实战:用串级PID搞定平衡小车的电机角度控制(附完整代码)

张开发
2026/4/18 23:09:03 15 分钟阅读

分享文章

STM32CubeMX实战:用串级PID搞定平衡小车的电机角度控制(附完整代码)
STM32CubeMX实战用串级PID搞定平衡小车的电机角度控制附完整代码平衡小车的姿态控制一直是嵌入式开发者热衷挑战的项目之一。不同于简单的电机转速控制平衡小车需要实时响应车身倾斜角度变化这对控制算法的响应速度和稳定性提出了更高要求。本文将带你从零开始基于STM32CubeMX和HAL库实现一套完整的串级PID控制系统让平衡小车稳稳立住。1. 平衡小车控制系统的特殊性平衡小车的核心在于姿态感知与快速响应。与普通电机控制不同平衡系统是一个典型的二阶不稳定系统——车身倾斜角度会直接影响电机转速而电机转速又会反过来改变车身角度。这种双向耦合关系使得传统单级PID难以胜任。1.1 为什么单级PID不够用试想一个简单场景当小车前倾时我们需要电机向前转动来恢复平衡。但单级PID直接控制角度时会出现两个典型问题响应滞后角度变化需要先转化为速度指令再通过PWM驱动电机环节过多导致响应延迟超调振荡当车身回正时由于惯性会继续向另一侧倾斜形成摇摆-矫正-反向摇摆的恶性循环// 单级PID角度控制的典型问题示例 void SinglePID_ProblemDemo() { float currentAngle MPU6050_GetAngle(); // 获取当前角度 float output PID_Calculate(anglePID, targetAngle, currentAngle); Motor_SetPWM(output); // 直接输出到电机 }1.2 串级PID的解决方案串级PID通过速度内环角度外环的双层控制架构完美解决了上述问题内环速度环快速响应电机转速变化抑制机械振动外环角度环维持整体姿态稳定处理外部干扰提示速度环相当于给系统增加了电子阻尼有效抑制了车身摆动时的能量积累2. 硬件系统搭建2.1 核心组件选型组件类型推荐型号关键参数备注主控芯片STM32F103C8T672MHz Cortex-M3最小系统板即可姿态传感器MPU6050±2000°/s陀螺仪需软件滤波电机驱动TB6612FNG1.2A持续电流支持PWM调速编码器光电增量式500线/转建议AB相输入2.2 STM32CubeMX配置要点定时器配置TIM1/TIM8高级定时器用于PWM生成TIM2/TIM3/TIM4编码器接口模式TIM6/TIM7基础定时器用于控制周期外设初始化// CubeMX生成的PWM初始化片段 htim1.Instance TIM1; htim1.Init.Prescaler 71; // 1MHz计数频率 htim1.Init.CounterMode TIM_COUNTERMODE_UP; htim1.Init.Period 999; // 1kHz PWM频率 HAL_TIM_PWM_Init(htim1);中断优先级安排最高优先级MPU6050数据就绪中断次高优先级编码器计数定时器最低优先级PID计算周期定时器3. 软件架构设计3.1 控制流程图解[角度传感器] → [角度环PID] → [速度环PID] → [电机PWM] ↑ ↓ ↑ [实际角度] [目标速度] [实际速度]3.2 核心数据结构typedef struct { PID_TypeDef angle; // 角度环PID PID_TypeDef speed; // 速度环PID float output; // 最终输出 } CascadePID; typedef struct { int32_t encoder; // 编码器累计值 float velocity; // 计算得到的速度 float target_angle; // 目标角度 CascadePID pid; // 串级PID控制器 } BalanceMotor;3.3 关键算法实现3.3.1 串级PID计算函数void CascadePID_Update(CascadePID *pid, float angle_ref, float angle_fdb, float speed_fdb) { // 外环角度计算 float speed_ref PID_Calculate(pid-angle, angle_ref, angle_fdb); // 内环速度计算 pid-output PID_Calculate(pid-speed, speed_ref, speed_fdb); // 输出限幅 pid-output constrain(pid-output, -MAX_PWM, MAX_PWM); }3.3.2 电机控制任务void Motor_ControlTask(void) { static uint32_t last_tick 0; if(HAL_GetTick() - last_tick CONTROL_PERIOD) return; last_tick HAL_GetTick(); // 获取传感器数据 float current_angle Sensor_GetAngle(); float current_speed Encoder_GetSpeed(); // 更新PID CascadePID_Update(motor.pid, motor.target_angle, current_angle, current_speed); // 输出到电机 Motor_SetPWM(motor.pid.output); }4. 调参实战技巧4.1 分阶段调试法先调速度内环暂时屏蔽角度环直接给定目标速度先调P值直到电机开始振动然后回退20%逐步加入I值消除静差再调角度外环P值从小开始每次增加0.5观察小车从倾斜到直立的过程出现振荡时适当加入D项4.2 常见问题排查表现象可能原因解决方案小车快速倒下角度环P值太小逐步增大P值持续低频摆动速度环阻尼不足增加速度环D值高频抖动采样周期过短降低控制频率或增加滤波单侧偏移机械重心不正调整配重或加入死区补偿4.3 高级优化技巧动态参数调整根据倾斜角度自适应改变PID参数if(fabs(current_angle) 15.0f) { // 大角度时使用更激进参数 pid.angle.Kp EMERGENCY_KP; } else { // 小角度时使用精细参数 pid.angle.Kp NORMAL_KP; }速度前馈预测性补偿车身运动趋势float feedforward current_angle * FEEDFORWARD_GAIN; pid.output constrain(feedforward, -MAX_FEEDFORWARD, MAX_FEEDFORWARD);5. 完整项目代码架构/Balance_Car │── /Core │ ├── Src │ │ ├── main.c # 主循环和任务调度 │ │ ├── pid.c # PID算法实现 │ │ ├── motor.c # 电机驱动 │ │ └── sensor.c # 传感器处理 │ └── Inc # 对应头文件 │── /Drivers │── /Middlewares │ └── /MPU6050 # 陀螺仪驱动 └── /STM32CubeMX # 工程配置文件关键代码片段已托管至GitHub仓库链接见文末包含完整的串级PID实现MPU6050DMP姿态解算编码器速度计算上位机调试接口6. 实测效果与优化方向经过实际测试采用本文方法的平衡小车可以实现静态直立误差 ±1.5°抗干扰能力轻推恢复时间 0.8s低速移动时的姿态保持进一步优化可以考虑加入蓝牙APP控制接口实现循迹或避障功能扩展移植到更强大的STM32H7平台提升控制频率最后分享一个调参时的小发现当车身使用软性材料包裹时由于增加了自然阻尼速度环的D值可以适当减小这比单纯提高电子阻尼效果更自然。

更多文章