大家好,欢迎来到IT知识分享网。
STM32 Cubemax(十五) —— 串级PID以控制电机角度值为例
前言
很早前说要补的坑,今天补一下。之前介绍过单级的PID来控制电机的速度值,建议先看下面这篇文章,因为后面代码和这篇文章有关联!
STM32 Cubemax(七) —— 单级PID控制带编码器的直流减速电机速度
而这次,我们来讲解一下怎么控制电机的角度,如果用单级PID控制角度会有什么问题,为什么要用串级PID。
当然串级PID不只是可以控制电机的角度,如现在诸多的控制系统,倒立摆,风力摆,平衡小车等等,都是基于串级PID的控制。
一、单级PID控制电机角度
如果还用我们之前熟知的单级PID来控制角度值,我们很容易的可以得出以下框图。
看上去很完美,没有什么问题,但这里面最大的问题就在于单片机给电机的是PWM值(或者电压值)并不是直接给定电机旋转的角度值,而可以认为是经过一个函数变换f(PWM)得出的值。
比如这里以角度为例,PWM输出的值,经过一个g(x)变换得到速度值,速度值经过h(x)(即积分)得到角度值,f(PWM)即为h(g(x))。
根据上述分析可知,对于简单的系统来说,这个f函数的变换,可以用较好的PID参数来抵消掉其中的误差量。但当系统变的复杂,比如平衡小车中,控制的角度不仅是单纯电机的角度,而是车上的位姿姿态,这种时候f(x)就较为复杂!这种时候,十分难得到一个PID参数来使系统稳定。
所以当我们遇到控制问题时,先看看这个f函数,是不是属于我上述说的情况,来看看单级PID是否可以使用。
我们接下来引入串级PID来解决上述f函数出现的问题。
二、串级PID
其实单级PID来控制角度,出现的具体问题就是控制反馈不全导致的!
以控制角度为例,其中最大的问题不在于h(x),因为h(x)相当于速度对时间的积分,问题在于g(x)的存在,输出的PWM会经过g(x)得出一个不可控的速度值(因为存在电机阻尼,或者外届阻力等),那么此时经过h(x)得出的角度值,也不会如我们想控制的一样。
那么解决方案就很简单了,就是去控制这个速度值,使其输出的速度值是我们控制器想要得到的值即可了。我们容易得出下面框图。
这里我们加了一级速度级PID来控制电机的输出速度,外面外环仍然使用一级角度级PID来控制电机最终速度角度,这就是串级的意义。
其本质内涵,可以认为速度环的引入,相当于给系统增加了阻尼,即抑制了g(x)中因为电机阻尼,外界阻尼等因素的影响。
三、串级PID代码
其实如果看懂上面串级PID的框图后,代码编写也十分简单了,就是相当于计算两次单级PID,其中外环角度环的目标值为设定的角度,反馈值为电机反馈的角度值,内环速度环的输入即为外环角度环的输出,反馈为电机反馈的速度值。
电机结构体
串级PID结构体
typedef struct _CascadePID
{
PID inner; // 内环速度环PID
PID outer; // 外环角度环PID
float output;
}CascadePID;
在电机中加入串级PID
typedef struct _Motor
{
int32_t lastAngle; //上次计数结束时转过的角度
int32_t totalAngle; //总共转过的角度
int16_t loopNum; //电机计数过零计数
float speed; //电机输出轴速度
float targetSpeed; //添加设定的目标速度
CascadePID anglePID; //串级PID
}Motor;
串级PID计算
//串级PID计算,参数为串级PID指针,角度目标值,角度返回值,速度返回值
void PID_CascadeCalc(CascadePID *pid,float angleRef,float angleFdb,float speedFdb)
{
PID_SingleCalc(&pid->outer,angleRef,angleFdb); //外环角度环
PID_SingleCalc(&pid->inner,pid->outer.output,speedFdb); //内环速度环
pid->output=pid->inner.output;
}
串级PID初始化
即对内环和外环的参数进行初始化
PID_Init(&Motor.anglePID.inner,0,0.0,0,0,0);
PID_Init(&Motor.anglePID.outer,0,0,0,0,0,0);
然后外面只需要把Motor_Send里的单级PID计算更换成我们的串级PID计算,就完成了串级PID的计算,这里要解释一下就是这里我们角度的返回值,填的是totalAngle,即我们编码器总读出来的角度值,所以我们这里targetAngle是基于totalAngle上的累加值。
void Motor_Send(enum Mode mode)
{
float output = 0;
PID_CascadeCalc(&motor.anglePID, motor.targetAngle, motor.totalAngle, motor.speed);
output = motor.anglePID.output;
if(output > 0)
{
__HAL_TIM_SetCompare(&htim5, TIM_CHANNEL_2, (uint32_t)output);
IN1(0);
IN2(1);
}
else
{
__HAL_TIM_SetCompare(&htim5, TIM_CHANNEL_2, (uint32_t)(-output));
IN1(1);
IN2(0);
}
}
四、串级PID调参
我们编写完有关串级PID的代码程序后,就到了最关键的调参环节。我们采用的调参方式是按照内环稳定,再调外环的方式,由我们上述的分析也可知,只有内环速度环稳定后,角度环才有意义!
这里我们需要先利用上次的程序,把内环调好!!,是利用上次的函数,即只有单级PID控制速度的程序。
我们把单级速度环调好后,将参数填入串级PID的内环中,然后我们就可以开debug去调外环了。
外环的调节方式和内环其实是完全一致的,具体就不再阐述了,看上篇文章,先P再I,最后D,这些都是按自己的具体项目需求分析,看看到底用PI控制好,还是PD或者是PID控制效果好。
如果在调节外环的时候发现,不管怎么调都不理想,其中一个可能就是内环没有调好!!再次去精调内环的值。
总结
本文是根据自己的实际使用经历来写的,有可能有些地方表达不是很准确,如果有错误,请指正!
整个代码放到gitee上了代码链接
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/15097.html