目录
前言
1、该系列教程是基于stm32f103c8t6最小系统板的hal库开发,用最通俗易懂的方式手把手带你学会使用Pid算法的速度环、位置环以及速度位置串级pid。
2、出这一期Pid系列教程的想法是前段时间我参加了一个比赛,要用到串级Pid的功能,可是我发现网上的教程要么是零零散散的,没有集中一起讲解这三个功能的,要么就是写的内容不够简单直接,基础没那么好的同学学起来会很吃力。为了用最直观的步骤和最简单的代码带大家一起掌握使用pid控制电机的能力,我打算出一期完整的、通俗易懂的系列教程,同时也作为对我前段时间学习内容的总结。
3、Pid算法涉及到的内容很多,值得我们去深入地研究去发现它的美,pid算法的学习不仅仅是理论上的理解,要去实践,实现把速度、位置控制好的功能。移植好pid算法以后的调参也是一门学问呢!哈哈哈,所以掌握pid并不是一件容易的事情,不过我已经把串级pid实现了,请相信我会带着大家一起从头到尾一点一点实现!
本章节要实现的功能是使用pid位置环控制电机,最终达到的效果是:电机可以转动到特定的位置,要求位置准确,抗干扰能力强(转到目的角度以后,用手很难转动电机)。
大家会学会:使用PID位置环控制电机;
学习本章前建议大家先学习完:【手把手带你用pid算法控制电机】——(1)编码器电机和0.96寸OLED显示屏的使用
在学会速度环的基础上,位置环就很容易掌握了;
1. PID位置环原理
位置环用的是位置式PID算法,什么是位置式PID在我的这篇文章:位置式Pid和增量式Pid的定义及应用
已经讲的比较清楚,这里总结就是一句话:位置环原理就是将每次进入中断以后采集到的脉冲值累加,得到的值就是电机转动了多少位置的反映;通过设置位置目标值,通过PID算法就可以实现将电机转到固定的目标位置。
2. 代码
本教程的代码是基于【手把手带你用pid算法控制电机】——(2)PID速度环
不需要再写代码,只需要在那篇文章的代码中改一些地方就行。
改下面这些代码,把关于位置式PID的代码取消注释,有关速度环PID的代码注释。
/* USER CODE BEGIN PV */
int16_t speed,encoder_counter;
float Position_KP=0.18,Position_KI=0.002,Position_KD=0; //位置PID系数
//float Velocity_KP=4.5,Velocity_KI=0.1,Velocity_KD=0; //速度PID系数
//int Encoder,Target_Velocity=30;
int Moto,Position_Moto;//电机PWM变量
//int limit_a;
//int Position,Target_Position=850; //位置和目标位置自己设定
/* USER CODE END PV */
/* USER CODE BEGIN 0 */
/**
* @function: void GET_NUM(void)
* @description: 使用STM32编码器模式,读取编码器产生的脉冲值
* @param {*}
* @return {*}
*/
void GET_NUM(void)
{
encoder_counter=(short) __HAL_TIM_GET_COUNTER(&htim3);
__HAL_TIM_SET_COUNTER(&htim3,0);//将编码器模式的定时器清零
}
/**
* @function:void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
* @description: 定时器中断回调函数,0.1S中断一次并计算转速,将电机转速以及编码器产生的脉冲数显示在OLED屏上
* @param {TIM_HandleTypeDef *htim}
* @return {*}
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==&htim1)
{
//key_flag=1;
GET_NUM();//得到所记录的脉冲数
Position+=encoder_counter;//脉冲数积累
Position_Moto = Position_PID(Position,Target_Position);
//limit_a=Xianfu(Position_Moto,myabs(Target_Velocity));
//Moto = Incremental_PI(encoder_counter,limit_a);
//Moto = Incremental_PI(encoder_counter,Target_Velocity);
Moto =Xianfu(Position_Moto,(这里填PWM满幅));
Set_Pwm(Moto);
//speed=(float)encoder_counter/2040/0.1;//转速为n,r/s 脉冲数转化为速度
//OLED_Showdecimal(0,4,speed,2,2,12,0);//在特定位置显示2位整数+2位小数的电机转速
}
}
/*串口重定向*/
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int _io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__*/
/******************************************************************
*@brief Retargets the C library printf function to the USART.
*@param None
*@retval None
******************************************************************/
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch,1,0xFFFF);
return ch;
}
/* USER CODE END 0 */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
printf("%d,%d\n",Position,Target_Position); //输出编码器的值(实际值)和目标值到vofa软件
}
3. 调参
我调出来的参数是
float Position_KP=0.18,Position_KI=0.002,Position_KD=0; //位置PID系数
pid参数的整定需要实际情况实际操作,注意:
(1)速度环的pid参数与位置环的pid参数不是同一组参数,算法也有所区别;
(2)但是它俩都是pid算法,那p、i、d各参数值的作用是一样的,调参的方法是一样的,最后效果都是要实际值去靠近目标值 ,越接近说明参数调的越好。
(3)位置环一般不用d值。
码字不易,希望喜欢的小伙伴别忘了点赞+收藏+关注,你们的肯定就是我创作的动力。
欢迎大家积极交流,本文未经允许谢绝转载!!!