STM32CubeMX(15) —— 串级PID以控制电机角度值为例

本文详细介绍了如何使用串级PID控制电机的角度。首先,分析了单级PID控制电机角度的问题,指出由于PWM到角度的转换函数不易控制。接着,引入串级PID,通过增加速度环来改善控制效果,确保输出速度符合预期。文章提供了串级PID的代码实现,包括电机结构体、PID计算和初始化,并分享了调参方法,强调先稳定内环再调外环。最后,提到了实际应用中的调参经验,并提供了代码链接。
摘要由CSDN通过智能技术生成

串级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上了代码链接

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lzzzzzzm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值