位置式PID与增量式PID的代码实现与电机控制实战
1. PID控制基础与电机控制场景PID控制是工业控制领域最经典的算法之一它通过比例P、积分I、微分D三个环节的组合实现对系统的精确控制。在电机控制中PID算法能够有效解决速度波动、负载变化等问题。比如当电机负载突然增大时传统开环控制会导致转速下降而闭环PID控制能自动调整PWM输出维持转速稳定。实际项目中我常用STM32的定时器模块实现PID控制。以直流有刷电机为例需要配置以下硬件资源编码器接口通过TIMx的编码器模式读取脉冲数如500线编码器每转产生2000个脉冲PWM输出使用TIMx的PWM模式生成驱动信号通常10kHz-20kHz频率定时中断用于固定周期的PID计算典型周期1-10ms2. 位置式PID的代码实现位置式PID的特点是输出值与执行机构位置一一对应适合需要绝对控制的场景。其离散化公式为u(k) Kp*e(k) Ki*∑e(j) Kd*[e(k)-e(k-1)]在STM32中的实现步骤如下2.1 定义PID结构体typedef struct { float Target; // 目标值 float Current; // 当前值 float Err; // 当前误差 float Err_Last; // 上次误差 float Kp,Ki,Kd; // PID系数 float Integral; // 积分项 float Output; // 输出值 uint32_t Timestamp; // 时间戳(用于微分项计算) } PositionalPID;2.2 PID核心算法float PositionalPID_Update(PositionalPID *pid, float current_val) { // 计算时间间隔(单位秒) float dt (HAL_GetTick() - pid-Timestamp) / 1000.0f; pid-Timestamp HAL_GetTick(); // 误差计算 pid-Err pid-Target - current_val; // 积分项(带抗饱和处理) pid-Integral pid-Err * dt; if(pid-Integral INTEGRAL_MAX) pid-Integral INTEGRAL_MAX; else if(pid-Integral -INTEGRAL_MAX) pid-Integral -INTEGRAL_MAX; // 微分项 float derivative (pid-Err - pid-Err_Last) / dt; // PID输出 pid-Output pid-Kp * pid-Err pid-Ki * pid-Integral pid-Kd * derivative; // 保存本次误差 pid-Err_Last pid-Err; return pid-Output; }2.3 定时器中断集成void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim htim6) { // 10ms定时器 // 读取编码器值(位置反馈) int32_t encoder_cnt TIM4-CNT; TIM4-CNT 0; // 计数器清零 // 位置式PID计算 float pwm PositionalPID_Update(pos_pid, encoder_cnt); // PWM输出限幅 pwm (pwm 1000) ? 1000 : (pwm -1000) ? -1000 : pwm; // 更新PWM输出 __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, abs(pwm)); HAL_GPIO_WritePin(MOTOR_DIR_GPIO_Port, MOTOR_DIR_Pin, (pwm 0) ? GPIO_PIN_SET : GPIO_PIN_RESET); } }3. 增量式PID的代码实现增量式PID输出的是控制量的增量特别适合步进电机等执行机构。其公式为Δu(k) Kp*[e(k)-e(k-1)] Ki*e(k) Kd*[e(k)-2e(k-1)e(k-2)]3.1 增量式PID结构体typedef struct { float Target; // 目标值 float Current; // 当前值 float Err; // 当前误差 float Err_Last; // 上次误差 float Err_BeforeLast; // 上上次误差 float Kp,Ki,Kd; // PID系数 float Output; // 输出增量 } IncrementalPID;3.2 算法实现float IncrementalPID_Update(IncrementalPID *pid, float current_val) { // 误差计算 pid-Err pid-Target - current_val; // 计算增量 pid-Output pid-Kp * (pid-Err - pid-Err_Last) pid-Ki * pid-Err pid-Kd * (pid-Err - 2*pid-Err_Last pid-Err_BeforeLast); // 更新误差记录 pid-Err_BeforeLast pid-Err_Last; pid-Err_Last pid-Err; return pid-Output; }3.3 速度环控制实例void SpeedControlLoop(void) { static float total_pwm 0; // 获取速度值(脉冲数/采样周期) int16_t speed (int16_t)TIM4-CNT; TIM4-CNT 0; // 增量PID计算 float delta_pwm IncrementalPID_Update(inc_pid, speed); // 累计输出 total_pwm delta_pwm; // 输出限幅 total_pwm (total_pwm 1000) ? 1000 : (total_pwm -1000) ? -1000 : total_pwm; // 更新PWM __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, abs(total_pwm)); HAL_GPIO_WritePin(MOTOR_DIR_GPIO_Port, MOTOR_DIR_Pin, (total_pwm 0) ? GPIO_PIN_SET : GPIO_PIN_RESET); }4. 两种PID算法的对比与选型4.1 性能对比表格特性位置式PID增量式PID输出性质绝对量增量积分处理需要累积误差无累积误差抗饱和需额外处理天然抗饱和计算量较大较小适用场景位置控制、阀门控制速度控制、步进电机对突变的响应可能产生大幅跳动变化平缓4.2 选型建议位置式PID更适合需要精确位置控制的场景如机械臂关节执行机构不带积分部件如伺服阀系统允许较大的计算开销增量式PID更适合速度控制场景如直流电机调速执行机构本身具有积分特性如步进电机需要避免积分饱和的场合MCU资源有限的场景5. PID参数整定实战技巧5.1 试凑法步骤先调P将Ki和Kd设为0逐渐增大Kp直到系统出现等幅振荡再调I取振荡周期的一半作为积分时间KiKp*(采样周期/Ti)最后调D一般取TdTi/4KdKp*(Td/采样周期)微调根据响应曲线对三个参数进行10%左右的调整5.2 常见问题处理振荡过大减小Kp或增大Kd响应迟缓增大Kp或减小Ki稳态误差适当增大Ki超调严重增大Kd或减小Kp5.3 自动整定代码示例void PID_AutoTune(PID *pid, float *input, float *output) { static uint8_t state 0; static float peak[4] {0}; static uint32_t last_cross 0; // 注入阶跃信号 if(HAL_GetTick() 2000) *output 100; else *output 0; // 检测振荡波形 if(*input pid-Target * 1.05) { if(state 0 || state 2) { peak[state] *input; state; } } else if(*input pid-Target * 0.95) { if(state 1) { peak[state] *input; state; last_cross HAL_GetTick(); } else if(state 3) { peak[state] *input; state 0; // 计算参数 float Ku 4*100/(3.1415*(peak[0]-peak[1])); float Tu (HAL_GetTick() - last_cross) / 1000.0f; // Ziegler-Nichols法 pid-Kp 0.6 * Ku; pid-Ki 1.2 * Ku / Tu; pid-Kd 0.075 * Ku * Tu; } } }6. 高级优化技巧6.1 积分抗饱和// 在位置式PID计算中加入 if(fabs(pid-Output) OUTPUT_MAX) { pid-Integral - pid-Err * dt; // 回退积分 }6.2 微分滤波// 一阶低通滤波 float alpha 0.2f; // 滤波系数 derivative alpha * derivative (1-alpha) * pid-LastDerivative; pid-LastDerivative derivative;6.3 变参数PID// 根据误差大小切换参数 if(fabs(pid-Err) 50) { // 大误差区间 pid-Kp 5.0; pid-Ki 0.1; } else { // 小误差区间 pid-Kp 2.0; pid-Ki 0.5; }7. 电机控制中的特殊处理7.1 死区补偿当PWM值较小时电机可能无法启动if(fabs(pwm) DEAD_ZONE) { pwm (pwm 0) ? DEAD_ZONE : -DEAD_ZONE; }7.2 速度测量滤波#define FILTER_LEN 5 static float speed_buf[FILTER_LEN] {0}; // 滑动平均滤波 float Filter_Speed(float new_speed) { static uint8_t idx 0; speed_buf[idx] new_speed; if(idx FILTER_LEN) idx 0; float sum 0; for(uint8_t i0; iFILTER_LEN; i) { sum speed_buf[i]; } return sum / FILTER_LEN; }7.3 串级PID实现位置环速度环的双闭环控制void CascadePID_Update(void) { // 外环(位置环) float speed_target PositionalPID_Update(pos_pid, encoder_cnt); // 内环(速度环) float current_speed Filter_Speed((float)TIM4-CNT); TIM4-CNT 0; float pwm IncrementalPID_Update(speed_pid, current_speed); // 更新输出 __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, abs(pwm)); }

相关新闻