直接来干货吧
对于单片机来说 pid 是一个 每隔多长时间执行一次 的 代码块 而这个时间通常就是积分时间
我们令这个代码快这个代码快执行周期为15ms,
那么通常我们单片机获取速度 的更新时间需要小于15ms
也就是说 采样周期 <= 积分时间
pid输入值更新的时间 < pid代码块执行的时间
(我采用的每次数据发生更新(中断函数),就立刻行一次pid 并把输出量转化为pwm,以提高效率)
pid控制减速电机输入量是速度(V)目标速度(Vm)
输出量就是pwm的占空比了(经验来看减速电机的pwm周期尽量短)
pid输出量 = P部分 + I部分 + D部分
P部分 = p值 x 误差值;
D部分 = d值 x (这一次的 V - 上一次的V) //(误差为 0 是 D部分为0)我们减速电机控制不需要考虑D
I部分 = I值(之前每次执行一次pid就把误差累加)//(积分)
//(最后误差为0的时候 I 值得积分记累加值就是输出量,以为P值和D值此刻已经为 0了)
代码大概如下:
pid 参数的整定:
SPid SPidStruct1;
SPidStruct1.dState = 0 ;
SPidStruct1.iState = 0 ;
SPidStruct1.iMAX = 900 ;
SPidStruct1.iMin = -900 ; // 采用系统输出范围的 90 %;
SPidStruct1.pid_P = 3.45 ; // 4.7 I D为0时开始震荡 取 70% 3.29
SPidStruct1.pid_I = 3 ;
SPidStruct1.pid_D = 0 ;
while(1)
{
a=UpdatePID(&SPidStruct1, 目标值-但前值 , 当前值 );
set_PWM(a);
delay_ms(10);
}
#include "pid.h"
typedef struct
{
double dState; // 上一次的 实际待调值
double iState; // 比例系数 i 的积分(累加结果)
double iMAX; // 积分 i 的最大值
double iMin; // 积分i 的最小值
double pid_P; // 比例系数 P
double pid_I; // 比例系数 I
double pid_D; // 比例系数 D
}SPid;
double UpdatePID(SPid *pid,double error,double TurePosition)
{
double pTerm,iTerm,dTerm;
pTerm = pid->pid_P*error; // P部分计算
pid->iState += error;//对 误差 积分
if(pid->iState > pid->iMAX) // i 范围约束(提高稳定性)
pid->iState = pid->iMAX;
else if(pid->iState < pid->iMin)
pid->iState = pid->iMin;
iTerm = pid->pid_I* pid->iState; // I部分计算
dTerm = pid->pid_D* (TurePosition - pid->dState); // D
pid ->dState = TurePosition; // 对 误差d 微分处理
return pTerm+iTerm+dTerm; // 返回pid的输出值
}
pid 调参有点痛苦的,,
这里提供 一个工具给大家,使用这个就可以不用了解使用matlab里的pid工具了
串口波形助手 ,通过串口周期的打印数据描绘出曲线
使用格式 printf("A%d\r\nB%d\r\nC%d\r\nD%d\r\n",a,b,c,d);
链接:https://pan.baidu.com/s/1uHMCPOgy0iiKYNDyRZCn-w
提取码:miak