STM32 Cubemax(十五) —— 串级PID以控制电机角度值为例

STM32 Cubemax(十五) —— 串级PID以控制电机角度值为例STM32Cubemax(十五)——基于Cubemax的FreeRTOS移植与LED点灯测试提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加例如:第一章Python机器学习入门之pandas的使用提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录系列文章目录 前言 一、pandas是什么? 二、使用步骤 1.引入库 2.读入数据 总结前言提示:这里可以添加本文要记录的大概内容:例如:随着人工智能的不断发展

大家好,欢迎来到IT知识分享网。

STM32 Cubemax(十五) —— 串级PID以控制电机角度值为例

前言

很早前说要补的坑,今天补一下。之前介绍过单级的PID来控制电机的速度值,建议先看下面这篇文章,因为后面代码和这篇文章有关联!

STM32 Cubemax(七) —— 单级PID控制带编码器的直流减速电机速度

而这次,我们来讲解一下怎么控制电机的角度,如果用单级PID控制角度会有什么问题,为什么要用串级PID。

当然串级PID不只是可以控制电机的角度,如现在诸多的控制系统,倒立摆,风力摆,平衡小车等等,都是基于串级PID的控制。


一、单级PID控制电机角度

如果还用我们之前熟知的单级PID来控制角度值,我们很容易的可以得出以下框图。

STM32 Cubemax(十五) —— 串级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)得出的角度值,也不会如我们想控制的一样。

那么解决方案就很简单了,就是去控制这个速度值,使其输出的速度值是我们控制器想要得到的值即可了。我们容易得出下面框图。

STM32 Cubemax(十五) —— 串级PID以控制电机角度值为例

 这里我们加了一级速度级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

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信