位置式PID调节PWM占空比控制电动机转速的程序方案
2025-12-15
位置式PID调节PWM占空比控制电动机转速的程序方案
位置式PID控制原理
位置式PID算法的离散形式为:
其中:
:当前时刻控制输出(PWM占空比) :当前误差 = 目标转速 – 实际转速 :误差累积和(积分项) :PID参数
硬件配置
所需外设
- 编码器接口:测量电机转速
- PWM输出:控制电机驱动
- 定时器:用于定时采样和PID计算
引脚配置示例
// PWM输出引脚 (控制电机)
#define MOTOR_PWM_PIN GPIO_Pin_8
#define MOTOR_PWM_PORT GPIOA
#define MOTOR_PWM_TIM TIM1
// 编码器输入引脚
#define ENCODER_A_PIN GPIO_Pin_6
#define ENCODER_B_PIN GPIO_Pin_7
#define ENCODER_PORT GPIOA
#define ENCODER_TIM TIM3
完整程序代码
1. PID结构体定义
typedef struct {
float Target; // 目标转速
float Current; // 当前转速
float Error; // 当前误差
float LastError; // 上一次误差
float PreError; // 上上次误差
float Kp; // 比例系数
float Ki; // 积分系数
float Kd; // 微分系数
float Integral; // 积分累计
float IntegralMax; // 积分限幅
float Output; // PID输出
float OutputMax; // 输出限幅
float OutputMin; // 输出下限
} PID_TypeDef;
// 初始化PID参数
void PID_Init(PID_TypeDef *pid, float kp, float ki, float kd, float max_output)
{
pid->Kp = kp;
pid->Ki = ki;
pid->Kd = kd;
pid->Integral = 0;
pid->Target = 0;
pid->Current = 0;
pid->Output = 0;
pid->OutputMax = max_output;
pid->OutputMin = 0;
pid->IntegralMax = max_output * 0.8f; // 积分限幅为输出的80%
}
2. 位置式PID计算函数
// 位置式PID计算
float PID_Calculate(PID_TypeDef *pid, float target, float current)
{
float increment = 0;
// 更新目标值和当前值
pid->Target = target;
pid->Current = current;
// 计算误差
pid->PreError = pid->LastError;
pid->LastError = pid->Error;
pid->Error = pid->Target - pid->Current;
// 积分项处理(带积分限幅和抗积分饱和)
pid->Integral += pid->Error;
// 积分限幅
if (pid->Integral > pid->IntegralMax) {
pid->Integral = pid->IntegralMax;
} else if (pid->Integral < -pid->IntegralMax) {
pid->Integral = -pid->IntegralMax;
}
// 位置式PID计算
pid->Output = pid->Kp * pid->Error +
pid->Ki * pid->Integral +
pid->Kd * (pid->Error - pid->LastError);
// 输出限幅
if (pid->Output > pid->OutputMax) {
pid->Output = pid->OutputMax;
} else if (pid->Output < pid->OutputMin) {
pid->Output = pid->OutputMin;
}
return pid->Output;
}
3. 编码器速度测量
// 编码器相关变量
volatile int32_t EncoderCount = 0;
volatile int32_t LastEncoderCount = 0;
volatile float MotorSpeed = 0; // 转速 RPM
// 编码器计数读取(在编码器中断中调用)
void Encoder_Update(void)
{
EncoderCount = (int32_t)TIM_GetCounter(ENCODER_TIM);
TIM_SetCounter(ENCODER_TIM, 0); // 清零计数器
}
// 速度计算函数(在定时中断中调用,如10ms)
void Speed_Calculate(void)
{
static uint32_t last_time = 0;
uint32_t current_time = HAL_GetTick();
uint32_t time_diff = current_time - last_time;
if (time_diff >= 10) { // 10ms计算一次速度
int32_t pulse_diff = EncoderCount;
EncoderCount = 0;
// 计算转速 (RPM)
// 假设编码器分辨率: 1000脉冲/转
// 公式: RPM = (脉冲数 / 编码器分辨率) × (60000 / 采样时间ms)
MotorSpeed = (pulse_diff / 1000.0f) * (60000.0f / time_diff);
last_time = current_time;
}
}
4. PWM输出控制
// PWM初始化
void PWM_Init(void)
{
TIM_OC_InitTypeDef sConfigOC = {0};
// PWM频率设置 (例如: 10kHz)
htim1.Instance = TIM1;
htim1.Init.Prescaler = 84 - 1; // 84MHz/84 = 1MHz
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 100 - 1; // 1MHz/100 = 10kHz
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim1);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0; // 初始占空比0%
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
}
// 设置PWM占空比
void Set_PWM_Duty(float duty_cycle)
{
uint32_t pulse;
// 限制占空比范围 0~100%
if (duty_cycle < 0) duty_cycle = 0;
if (duty_cycle > 100) duty_cycle = 100;
// 计算PWM脉冲值
pulse = (uint32_t)(duty_cycle * (TIM1->ARR + 1) / 100.0f);
// 更新PWM占空比
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, pulse);
}
5. 主控制程序
PID_TypeDef MotorPID;
float TargetSpeed = 1000.0f; // 目标转速 1000 RPM
int main(void)
{
// 系统初始化
HAL_Init();
SystemClock_Config();
// 外设初始化
PWM_Init();
Encoder_Init(); // 编码器初始化(需要自己实现)
TIM_Init(); // 定时器初始化(用于定时采样)
// PID参数初始化(需要根据实际电机调试)
PID_Init(&MotorPID, 0.5f, 0.01f, 0.05f, 100.0f);
while (1)
{
// 在定时中断中调用Speed_Calculate()更新速度
// 主循环中可以处理目标速度设定等
// 每50ms执行一次PID控制
static uint32_t last_pid_time = 0;
if (HAL_GetTick() - last_pid_time >= 50) {
float pwm_output;
// PID计算
pwm_output = PID_Calculate(&MotorPID, TargetSpeed, MotorSpeed);
// 更新PWM输出
Set_PWM_Duty(pwm_output);
last_pid_time = HAL_GetTick();
// 可以通过串口输出调试信息
printf("Target: %.1f, Current: %.1f, PWM: %.1f%%\r\n",
TargetSpeed, MotorSpeed, pwm_output);
}
// 其他任务...
HAL_Delay(1);
}
}
6. 定时器中断处理
// 定时器中断回调函数(例如1kHz)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2) { // 假设TIM2用于定时采样
// 更新编码器计数
Encoder_Update();
// 计算速度(每10次调用计算一次速度)
static uint8_t speed_counter = 0;
if (++speed_counter >= 10) {
Speed_Calculate();
speed_counter = 0;
}
}
}
PID参数整定方法
手动整定步骤:
- 先调Kp:将Ki、Kd设为0,逐渐增大Kp直到系统开始振荡
- 再调Ki:加入Ki消除静差,但不要太大以免超调
- 最后调Kd:加入Kd抑制超调,改善动态性能
典型参数范围(需要根据实际电机调整):
// 直流有刷电机典型参数
#define KP_START 0.1f // 开始调试值
#define KI_START 0.001f
#define KD_START 0.01f
// 无刷电机典型参数
#define KP_BLDC 0.3f
#define KI_BLDC 0.005f
#define KD_BLDC 0.02f
参考代码 位置式PID调节PWM占空比控制电动机转速 www.3dddown.com/csa/70961.html
注意事项
- 采样时间选择:速度采样和PID计算周期要匹配,通常10-50ms
- 积分抗饱和:必须对积分项进行限幅
- 输出限幅:PWM占空比限制在安全范围内
- 编码器分辨率:根据实际编码器参数调整速度计算公式
- 电机死区:有些电机在低占空比时不转动,需要设置最小启动占空比
调试技巧
- 串口监控:实时输出目标速度、实际速度、PWM占空比
- 阶跃响应:观察系统对目标速度突变的响应
- 抗扰动测试:在电机运行时施加负载,观察恢复性能
