最近一直在学习PID的相关知识,网上有很多心得和口诀,看完过后感觉自己好像是懂了一些,但让自己实践一下又感觉无从下手,举手无措。所以今天我决定,直接上手一个简单的电机速度PID试试。
准备阶段
-
一块主控板子 我这里刚好有一块原子的mini开发板,所以就用它了。其实任何一块最简单的STM32核心板都能完成功能。
-
一个电机 我这里用的是平衡小车之家的减速比30:1的直流减速电机,需要注意的是需要带编码器作为位置反馈
-
一块电机驱动
电机驱动这里用的是L298N,使用非常简单。方向控制,其中一个高电平一个低电平就能控制电机转了,假设左边高是正转,那么右边高便是反转,同时为高或同时为低则不转。实际我们是输入两个脉冲波进行控制,脉冲的占空比决定转速。
- 电源供给
我这里用的是12V的航模电池,其实学生电源也行,不过单片机是需要3.3伏的电压,没有降压模块的同学可以用usb给板子供电(usb是5伏,不过板子上有降3.3伏的芯片),然后注意和航模电池共地就好了
- USB转TTL模块
(图片来自淘宝)这个用于单片机通过串口向电脑反馈数据,然后通过上位机软件显示波形,便于观察和调试。上位机我这里用的是匿名的科创地面站v4.34。软件可以在他们的官网下载。 地址
嫌麻烦的也可以直接看运动效果(~ ^_ ^~)
电路搭建
电路搭建比较简单,简单说一下,航模电池引出两路,一路通过降压模块给开发板和编码器供电,一路连接电机驱动L298N。开发板通过PA9,PA10连接USB转TTL模块。要注意的是电机的接口有6根线,两根是电机+和电机-,两根是编码器电源和地,两根是编码器A相B相。AB相连接PA6和PA7。然后电机驱动的方向控制连接PA0和PA1(图中还没连)。这里的电机+ -,方向的控制引脚,和编码器的AB连接引脚不用固定。只要后面软件设定的正转是你想要的正转就行了,不对就交换下引脚好了。
软件搭建
- PWM初始化 这些就没必要贴了,原子例程里一堆
- 编码器初始化
static void TIM3_Mode_Config(void)//TIM3编码器模式
//PA6 ch1
//PA7 ch2
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//- 正交编码器输入引脚 // PA6 A Pa7 B
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*- TIM4编码器模式配置 -*/
TIM_DeInit(TIM3);
TIM_TimeBaseStructure.TIM_Period = 65535;//设定计数器自动重装值
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12,
TIM_ICPolarity_BothEdge ,TIM_ICPolarity_BothEdge);
//配置编码器模式触发源和极性4倍频
TIM_ICStructInit(&TIM_ICInitStructure); //配置滤波器
TIM_ICInitStructure.TIM_ICFilter = 6;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ClearFlag(TIM3, TIM_FLAG_Update);//清除TIM的更新标志位
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //使能或者失能指定的TIM中断
TIM3->CNT = 0;//初始值为0
TIM_Cmd(TIM3, ENABLE); //启动TIM3定时器
}
- 串口初始化
串口初始化比较简单,不过串口通信协议需要按照匿名上位机的写,这个在上位机里有相关协议和例程。
-
定时器初始化
假定10ms控制一次
-
简单的PID程序
这大概是最简单的PID了
``
PID控制里一定要考虑的(重点)
网上的教程,原理讲的很透,但是实际运用起来,有些东西是必须要说明的
1 PWM的重装载值
这个值选择100或者10000,同一个函数运行起来,效果绝对是不一样的,本文选择1000(随缘取的,感觉不错,大家也可以选择100,10000,或者其他的~ ^ ^ ~),记住这个值,我们的PID控制输出最高值就应向这个值看齐
2 控制周期
这个值也非常重要,它既影响了控制效果(10ms控制一次和1s控制一次是不一样的),也影响了速度反馈的数值。我的速度反馈算法是最简陋的,算一个控制周期里编码器反馈的脉冲数。控制周期我选择 10ms,所以这里速度的单位为( cnt/10ms)
3 期望值的范围
本文的的期望值就是希望得到的速度,这个值不是随便给的,粗略计算一下,假设我们的电机一秒最多能5圈,假设1圈1560个脉冲,那么1s反馈的脉冲数为1560*5=7800,在10ms里最多78个脉冲,所以我们的期望值应设在 -78到78(小数就不考虑了) 之间(单位 cnt/10ms)
4 输出值的范围
因为我们的pwm最大值为1000,那么我希望我的PID控制在(0——1200)之间比较合适,略微超出的限下幅就很舒服,假如输出范围为(0——10000),这就不太合理,超过1000的值都被限幅了,相当于整个电机大部分时间都在满转速运动(就变成了开环控制),控制效果就不好了。
假如如我期望速度为30,而刚开始的速度反馈为0,那么第一次控制的偏差error就为30,一开始我们确实是希望电机能满电压运行,然后当速度提到7左右的时候,电机加速度就应该慢慢减弱,最后让速度能够平滑地变成10。那么这个Kp值大概多少比较好呢,首先Kp=1.0是没用的,30的pwm连电机都动不了~ … ~,那么10呢,初始输出为300,电机应该处于刚起转的地步,理论上的Kp值应该是10的数倍,不过我也是刚上手,先设个10.0看看效果
参数整定
东西已经弄好了,我们来试试效果吧
试试P=10,i=0,d=0的效果(期望速度为30cnt/10ms)
以下部分还待编辑
从图中我们可以看到看到,电机在转过3000后往回减了一点,然后就不动了,这说明存在静差。然后计算下时间,从哪开始动到稳定,2305-2168=137(看横坐标),就是1370ms,这便是调整时间。下一步,我们应该按照网上给的口诀,先调整P大到接近等幅震荡。
之后,加一些d,你会发现它没那么震荡了,这时候再稍稍调高一下p,如果对系统要求不高,就ok了。
这里加一句,p参数形象的理解就是响应速度,d就是阻尼。一个好的系统,既要有不错的响应,也应有适当的阻尼,稳定性才好。至于i,在pd调完之后,加一点,比如p参数的1/100,主要用于减少静态误差,但实际上我们都是动态控制,它可有可无。