电机增量式PID 控制(HAL 库)

主控芯片:STM32F103C8T6

电机型号:MD520Z30

电机驱动:TB6612

软件:CUBEMX

接线:

PA1==>>编码器A相

PA0==>>编码器B相

PA6==>>AIN1

PA7==>>AIN2

PB1==>>PWMA

①新建项目:

设置时钟72MHz

②配置定时器

TIM2设置为编码器模式:

TM3为驱动电机的PWM,这里我们打开通道四

由于要对电机进行测速,所以打开TIM4定时器,设置10ms定时,1 * 7200/72M * 100=10ms

中断配置:先打开TIM2,TIM4中断,再设置定时器优先级,TIM2优先级最高,因为要不断读取编码器数值

配置串口:选择USART1

③生成工程:

在ProjcetManager中,输入Project name ,Toolchain选择MDK_ARM,在Code Genderder,勾选上Generate peripheral initialization as a pair of'c/.h' fles per peripheral,再点击GENERATE CODE

点击魔术棒,勾选上USE  MocroLIB

先引入打印头文件

/* USER CODE BEGIN Includes */
#include  "stdio.h"
/* USER CODE END Includes */
重新定义打印函数
/* USER CODE BEGIN 0 */
int fputc(int ch, FILE *p)
{
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;

}
int fgetc(FILE* f)
{
  uint8_t ch;
  HAL_UART_Receive(&huart1,(uint8_t *)&ch,1,HAL_MAX_DELAY);
  return ch;
}
/* USER CODE END 0 */

打开对应通道

  /* USER CODE BEGIN 2 */
	HAL_TIM_Base_Start_IT(&htim4); 		//启动定时器器
	HAL_TIM_Base_Start_IT(&htim2);  	//启动编码器中断
	HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);	//打开编码器通道 
    HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);		//打开TIM3 通道
  /* USER CODE END 2 */

可以先给定pwm信号,测试电机转不转。

补充pid知识,本文用的是增量式pid,对应的公式:

其中Kp为比例系数,Ki为积分系数,Kd为微分系数,具体的可以去网上寻找相关资料。

回到工程,新建pid.c文件

#include <pid.h>

int16_t L_Encoder_Speed = 0;
int32_t Position = 0;
int16_t L_Output_Val; 
PID L_AddPID;
float speed,count;
void PID_Init(void)
{
    L_AddPID.target_val = 60;
    L_AddPID.output_val = 0.0;
    L_AddPID.Error = 0.0;
    L_AddPID.LastError = 0.0;
    L_AddPID.integral = 0.0;
    L_AddPID.Kp = 80;
    L_AddPID.Ki = 4;
    L_AddPID.Kd = 1.5; 

}

/**
  * @brief  速度PID算法实现
  * @param  actual_val:实际值
  *	@note 	无
  * @retval 通过PID计算后的输出
  */
float addPID_realize(PID *pid, float actual_val)
{
	/*计算目标值与实际值的误差*/
	pid->Error = pid->target_val - actual_val;
	/*PID算法实现,照搬公式*/
	pid->output_val += pid->Kp * (pid->Error - pid-> LastError) +
	                  pid->Ki * pid->Error +
	                  pid->Kd *(pid->Error -2*pid->LastError+pid->PrevError);
	/*误差传递*/
	pid-> PrevError = pid->LastError;
	pid-> LastError = pid->Error;
	/*返回当前实际值*/
	    if(pid->output_val>Max_Pid_Value)
    {
        pid->output_val=Max_Pid_Value;
        return Max_Pid_Value;
    }
			
	 else return pid->output_val;
}

	void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{

  //  4号定时器中断
  if(htim == (&htim4))
  {
    //  设置测量频率为100Hz(10ms)
    int rate = 100;
    //  获取编码器信号数
     L_Encoder_Speed = (int16_t)__HAL_TIM_GET_COUNTER(&htim2);
    //  计算10ms的速度
     count = ((float)L_Encoder_Speed)/4/11/30;//4:AB向上下沿采样;11:转动一圈11个脉冲;1:30的减速比(不同电机改对应参数)
    //  编码器数据清零
    __HAL_TIM_SET_COUNTER(&htim2, 0);

    //  计数1minute的转速
     speed = count * rate* 60;  //10ms*100*60=1minute

    L_Output_Val=addPID_realize(&L_AddPID,L_Encoder_Speed);
    __HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_4, L_Output_Val);
    
  }
}

对于不同电机,kp,ki,kd的值都应该不同的,大家可以参考网上调参教程

新建pid.h文件

#include <main.h>
#include <tim.h>
#define Max_Pid_Value 10000

typedef struct
{
	float target_val;   //目标值
	float Error;          /*第 k 次偏差 */
	float LastError;     /* Error[-1],第 k-1 次偏差 */
	float PrevError;    /* Error[-2],第 k-2 次偏差 */
	float Kp,Ki,Kd;     //比例、积分、微分系数
	float integral;     //积分值
	float output_val;   //输出值
}PID;


void PID_Init(void);
extern char Uart3_Rx_Buf[5];
float addPID_realize(PID *pid, float actual_val);

再回到main.c中,在  /* USER CODE END 2 */,    加入PID_Init();

在while 循环中,可加入printf函数,将数据打印到串口助手

接好线,烧录程序,接上电源,打开VOFA+串口,添加波形图控件,Y轴打开全部IO口,启动串口,即可看到波形图生成

中间波形图有波动是由于用手去给它阻力,但看出来还是能及时调整转速的。

END:

由于本人比较小白,PID算法是个很经典的东西,做平衡小车,飞控都会有所涉及到,但对于刚刚开始学习pid算法的我来说简直是一种折磨,实现此工程也花费了几天时间

本文记录是为了自己后期回顾,也可以给大家借鉴学习,有不足的地方欢迎大家留言!

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值