打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
ROS机器人电机PID控制

2.3 STM32电机PID速度控制

大家好,我是白茶.清欢。之前的几篇文章,完成了直流减速电机的PWM控制、电机测速。本篇文章,将实现电机的速度闭环控制。在公众号:小白学移动机器人,发送:速度PID,即可获得本篇文章的STM32工程文件以及相关资料。

2.3.1 解决的问题

解决带编码器电机的速度闭环问题。

2.3.2 PID理论

将偏差的比例、积分、微分,通过线性组合构成控制量,用控制量对被控对象进行控制,这样的控制器称为PID控制器。在连续空间中,我们通常探讨模拟PID的控制原理,如图所示:

我们这里用电机速度控制为例,讲解PID控制系统。r(t)为设定电机速度、y(t)为实际电机速度、e(t)=y(t)-r(t)为速度差值作为PID控制器的输入、u(t)为PID控制器的输出,作用到被控对象电机上。根据模拟PID控制器,科学家们也得出了模拟PID控制的公式,如图所示:

其中Kp、Ti、Td,分别为控制器的比例系数、积分系数、微分系数。该理论用在控制的例子比比皆是。但是模拟PID控制系统是在连续空间的上描述的,无法在计算机上用代码实现。于是就有数字PID控制理论,将连续空间的PID控制系统在离散空间上描述。积分变成了求和、微分变成了求斜率,于是就出现数字PID控制系统的理论公式,如图所示:

其中Kp、Ti、Td和上面描述的一样,T为采用周期,ek是本次差值,ek-1上一次的差值,直接通过模拟PID转化的数字PID又叫做位置式PID,该方式的PID的输出直接是控制量,非常不适合经常出现异常的系统,另外一种方式是增量式PID,每次只输出一个正向或者反向的调节量,就算出现异常,也不会产生巨大的影响。具体数学公式如下所示:该方法较多的应用于生产生活中,本论文中电机的速度PID控制当然也不例外。

有了上面的理论基础,开始代码实现的介绍。首先就是明确增量式PID系统的输入、输出、控制对象。将速度的设定值和速度的测得值作为PID控制器的输入参数,PID的输出参数为对PWM的调节偏差,控制对象PWM进而驱动电机达到设定速度。以上内容确定之后,就是PID控制器的代码部分了。其实仔细看看增量式PID就只有一个公式,所以使用代码实现并不困难。如下所示核心代码就这一句。

完成上面的代码,只是完成速度PID的一部分,剩下的是尤为重要的PID参数整定。该整定方法丰富多样,最为准确的是模型计算,但是对于我们做机器人多使用试凑法。虽然需要调节一段时间,但是不需要对机器人进行建模。试凑法一般按照P、I、D的顺序进行调节。

初始时刻将Ki和Kd都设置成0,按照经验设置Kp的初始值,就这样将系统投入运行,由小到大调节Kp。求得满意的曲线之后,若要引入积分作用,将Kp设置成之前的5/6,然后Ki由小到大开始调节。达到满意效果之后,若要引入微分作用,将Kd按照经验调节即可。经过有规律的试凑,最终达到一个我们满意的就行。

2.3.3 代码分享

关于PWM控制、以及电机测速的代码,这里就不再展示了,看之前的文章都可以找到。

(1)pid.h

#include 'pid.h'

/*===================================================================
程序功能:双路电机速度PID
程序编写:公众号:小白学移动机器人
其他   :如果对代码有任何疑问,可以私信小编,一定会回复的。
=====================================================================
------------------关注公众号,获得更多有趣的分享---------------------
===================================================================*/


struct pid_uint pid_Task_Letf;
struct pid_uint pid_Task_Right;

/****************************************************************************
*函数名称:PID_Init(void)
*函数功能:初始化PID结构体参数
****************************************************************************/


void PID_Init(void)
{
//乘以1024原因避免出现浮点数运算,全部是整数运算,这样PID控制器运算速度会更快
/***********************左轮速度pid****************************/
 pid_Task_Letf.Kp = 1024 * 0.5;//0.4
  pid_Task_Letf.Ki = 1024 * 0
 pid_Task_Letf.Kd = 1024 * 0.08
 pid_Task_Letf.Ur = 1024 * 4000;
 pid_Task_Letf.Adjust   = 0;
 pid_Task_Letf.En       = 1;
 pid_Task_Letf.speedSet = 0;
 pid_Task_Letf.speedNow = 0;
 reset_Uk(&pid_Task_Letf);  
/***********************右轮速度pid****************************/
 pid_Task_Right.Kp = 1024 * 0.35;//0.2
  pid_Task_Right.Ki = 1024 * 0//不使用积分
 pid_Task_Right.Kd = 1024 * 0.06
 pid_Task_Right.Ur = 1024 * 4000;
 pid_Task_Right.Adjust   = 0;
 pid_Task_Right.En       = 1;
 pid_Task_Right.speedSet = 0;
 pid_Task_Right.speedNow = 0;
 reset_Uk(&pid_Task_Right);
}

/***********************************************************************************************
 函 数 名:void reset_Uk(PID_Uint *p)
 功    能:初始化U_kk,ekk,ekkk
 说    明:在初始化时调用,改变PID参数时有可能需要调用
 入口参数:PID单元的参数结构体 地址
************************************************************************************************/


void reset_Uk(struct pid_uint *p)
{
 p->U_kk=0;
 p->ekk=0;
 p->ekkk=0;
}

/***********************************************************************************************
 函 数 名:s32 PID_commen(int set,int jiance,PID_Uint *p)
 功    能:PID计算函数
 说    明:求任意单个PID的控制量
 入口参数:期望值,实测值,PID单元结构体
 返 回 值:PID控制量
************************************************************************************************/


s32 PID_common(int set,int jiance,struct pid_uint *p)
{
 int ek=0,U_k=0;

 ek=jiance - set;                                                               
 
 U_k=p->U_kk + p->Kp*(ek - p->ekk) + p->Ki*ek + p->Kd*(ek - 2*p->ekk + p->ekkk);
 
 p->U_kk=U_k;
    p->ekkk=p->ekk;
 p->ekk=ek;
 
 if(U_k>(p->Ur))                                      
  U_k=p->Ur;
 if(U_k<-(p->Ur))
  U_k=-(p->Ur);
 
 return U_k>>10
}

/***********************************************************************************
** 函数名称 :void Pid_Which(struct pid_uint *pl, struct pid_uint *pr)
** 函数功能 :pid选择函数       
***********************************************************************************/


void Pid_Which(struct pid_uint *pl, struct pid_uint *pr)
{
 /**********************左轮速度pid*************************/
 if(pl->En == 1)
 {         
  pl->Adjust = -PID_common(pl->speedSet, pl->speedNow, pl);  
 } 
 else
 {
  pl->Adjust = 0;
  reset_Uk(pl);
  pl->En = 2
 }
 /***********************右轮速度pid*************************/
 if(pr->En == 1)
 {
  pr->Adjust = -PID_common(pr->speedSet, pr->speedNow, pr);  
 } 
 else
 {
  pr->Adjust = 0;
  reset_Uk(pr);
  pr->En = 2
 }
}

/*******************************************************************************
 * 函数名:Pid_Ctrl(int *leftMotor,int  *rightMotor)
 * 描述  :Pid控制
 *******************************************************************************/


void Pid_Ctrl(int *leftMotor,int  *rightMotor)
{
 Pid_Which(&pid_Task_Letf, &pid_Task_Right); 
 *leftMotor  += pid_Task_Letf.Adjust;
 *rightMotor += pid_Task_Right.Adjust;
}


(2)main.c

#include 'sys.h'

//====================自己加入的头文件===============================
#include 'delay.h'
#include 'led.h'
#include 'encoder.h'
#include 'usart3.h'
#include 'timer.h'
#include 'pwm.h'
#include 'pid.h'
#include 'motor.h'
#include <stdio.h>
/*===================================================================
程序功能:直流减速电机的速度闭环控制测试
程序编写:公众号:小白学移动机器人
其他    :如果对代码有任何疑问,可以私信小编,一定会回复的。
=====================================================================
------------------关注公众号,获得更多有趣的分享---------------------
===================================================================*/

int leftSpeedNow  =0;
int rightSpeedNow =0;

int leftSpeeSet   = -300;//mm/s
int rightSpeedSet = -300;//mm/s

int main(void)


 GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁用JTAG 启用 SWD
 
 MY_NVIC_PriorityGroupConfig(2); //=====设置中断分组
 
 delay_init();              //=====延时函数初始化
 LED_Init();                     //=====LED初始化    程序灯 
 
 usart3_init(9600);              //=====串口3初始化  蓝牙 发送调试信息

 Encoder_Init_TIM2();            //=====初始化编码器1接口
 Encoder_Init_TIM4();            //=====初始化编码器2接口
 
 Motor_Init(7199,0);             //=====初始化PWM 10KHZ,用于驱动电机 如需初始化驱动器接口
 
 TIM3_Int_Init(50-1,7200-1);     //=====定时器初始化 5ms一次中断

 PID_Init();      //=====PID参数初始化
 
 while(1)
 {
  //给速度设定值和实时值赋值
  pid_Task_Letf.speedSet  = leftSpeeSet;
  pid_Task_Right.speedSet = rightSpeedSet;
  pid_Task_Letf.speedNow  = leftSpeedNow;
  pid_Task_Right.speedNow = rightSpeedNow;
  
  //执行PID控制函数
  Pid_Ctrl(&motorLeft,&motorRight);
  
  //根据PID计算的PWM数据进行设置PWM
  Set_Pwm(motorLeft,motorRight);
  
  //打印速度
  printf('%d,%d\r\n',leftSpeedNow,rightSpeedNow);
 } 
}

//5ms 定时器中断服务函数

void TIM3_IRQHandler(void)                            //TIM3中断
{
 if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否
 {
  TIM_ClearITPendingBit(TIM3, TIM_IT_Update);   //清除TIMx的中断待处理位
  
  Get_Motor_Speed(&leftSpeedNow,&rightSpeedNow);//计算电机速度
  
  Led_Flash(100);                               //程序闪烁灯
 }
}

2.3.4 总结

以上三篇内容,是关于直流减速电机的PWM控制、速度测量以及最后电机速度的闭环控制。现在对于电机的简单控制基本告一段落,对于做一个ROS小车的电机控制,这里基本是够的。下面我们会介绍使用DMP获取MPU6050数据。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
pwm控制电机正反转,PWM直流电机调速程序
这么简单做梦能笑醒,四部教你单片机实现真正的多线程的最优解
软件中PID与PWM的占空比之间的关系
用单片机控制步进电机与直流电机其原理是否一样?
无刷直流电动机工作原理及其优化控制
【分享】增量式PID的stm32实现,整定过程 (amobbs.com 阿莫电子论坛)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服