基于STM32F103C6T6的AB相霍尔编码电机的PID转速调节(CubeMx-HAL库)(未完成-持续更新)

2023-10-26

基于STM32F103C6T6的AB相霍尔编码电机的PID转速调节(CubeMx-HAL库)(未完成-持续更新)

主要是记录一下,以后忘了再来看看,也记录记录自己做过的东西

首先是硬件电路图,一下是驱动板的硬件电路图(来自于实验室的某大佬比赛开的BTN驱动 再说一遍不是我开的)图省事直接拿过来用了,到程序调的差不多了我会开一版新的驱动和主控。
以前自己也开了一套MOS的H桥有刷驱动,但是自己手贱,明明知道自己设计的是12V的驱动,偏就直接要怼24V的大疆电池,依稀记得大一的时候,一个大二学长开的主控,也是被我怼的24V,还有不多几天就电赛了,学长是连夜修啊,惭愧。

来看看硬件的原理图
在这里插入图片描述
此处电机用的是AB相霍尔编码电机如下
请添加图片描述
然后就是接线,电机上都写的清楚,我也就不细说了

之后就是CubeMx的配置,时钟树如下
在这里插入图片描述
先是PWM输出口,一共开俩一个控制正转一个控制反转,我配置的PWM频率是1KHz就差不多,满占空比的CCR值是999,然后根据原理图做出以下配置。
至于PWM频率的计算,公式是这个 PWMf = TIMf/(ARR+1)*(PSC+1)
在这里插入图片描述
然后就是AB相编码器的配置,刚开始不知道芯片自带解算,可以直接读到电机旋转方向和计数值,用的中断触发然后判断另一相的状态,以此获取正反转和计数值,CubeMx是可以直接配置AB相编码器的啊,然后具体配置如下,当时为啥选8191为溢出值,啊这个没啥关系,一般我们采样间隔时间越长他的电机编码器的计数值就越大,让计数值不要超过这个溢出值就行。
在这里插入图片描述
然后就是串口啊,串口配置默认就行,后面调PID的三个初始化值会用到串口,实验室的大佬写了上位机的调参软件(对就是上面开驱动这个大佬),开了DMA
在这里插入图片描述
DMA
在这里插入图片描述
中断
在这里插入图片描述
关掉CubeMx默认生成的回调函数(大佬怎么说就怎么做,毕竟是人家写的上位机,不用调一次参,下载一遍程序是真的香)
在这里插入图片描述
然后是CAN这个CAN我是准备与主控连接的,主控通过CAN发送目标值,由驱动板进行PID调节。
此处CAN的频率为1MHz,为啥选1MHz,当时是希望兼容Robomaster的程序的(没错画电路板的那个大佬又写了Robomatser的整套程序)
在这里插入图片描述
CAN开一个接收中断用于处理主机发送过来的速度目标值
在这里插入图片描述
然后开个Freertos嗯,大佬说没有Freertos不好玩,开!
都是默认,也没有再添加线程,在程序里自己写添加线程
在这里插入图片描述
然后配置,导出工程
在这里插入图片描述
在这里插入图片描述
之后就是软件代码编写
首先移植大佬的调参器线程,嗯,也没有经过大佬同意我就不放调参器线程函数了
之后就是电机编码器的软件代码

MotorBoard_Encoder.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : MotorBoard_PID.c
  * @brief          : MotorBoard_PID program body
  * @author         : Lesterbor
  * @time			: 2021-09-14	
  ******************************************************************************
  * @attention		: 此处的电机获取速度值 写在了freertos的默认线程中
  *
  *
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

  #include "MotorBoard_Encoder.h"
  #include "stdio.h"
  #include "tim.h"
	
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PT */

	signed short Speed = 0;
	
/* USER CODE END PT */

/* Function definition -------------------------------------------------------*/
/* USER CODE BEGIN FD */
/**
  * @Function name  : MotorBoard_Encoder_Init
  * @Introduce  	: 编码电机的PID调节初始化
  * @Return 		: Null
  */
	void MotorBoard_Encoder_Init(void){
			HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);
	}
	
/**
  * @Function name  : MotorBoard_Encoder_Getspeed
  * @Introduce  	: 获取编码器的值
  * @Return 		: 电机编码器值
  */
	unsigned short  MotorBoard_Encoder_GetSpeed(void){
		return __HAL_TIM_GET_COUNTER(&htim2);
	}
	
/**
  * @Function name  : MotorBoard_Encoder_SetZero
  * @Introduce  	: 编码器清零
  * @Return 		: NULL
  */
	void MotorBoard_Encoder_SetZero(void){
		__HAL_TIM_SET_COUNTER(&htim2,0);
	}
	
/* USER CODE END FD */
/************************ (C) CopyRight Lesterbor ******END OF FILE******/

MotorBoard_Encoder.h

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : MotorBoard_Encoder.h
  * @brief          : Header for MotorBoard_Encoder.c file.
  *                   This file provides code for the configuration
  *                   of the MotorBoard_Encoder instances
  * @author         : Lesterbor
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MOTORBOARD_ENCODER_H_
#define __MOTORBOARD_ENCODER_H_

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

  #include "main.h"
	 
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PT */

	

/* USER CODE END PT */

	
/* Exported functions prototypes ---------------------------------------------*/
/* USER CODE BEGIN EFP */

  void			MotorBoard_Encoder_Init(void);
  unsigned short  MotorBoard_Encoder_GetSpeed(void);
  void 			MotorBoard_Encoder_SetZero(void);
	
/* USER CODE END EFP */

#endif /* __MOTORBOARD_ENCODER_H_ */
/************************ (C) CopyRight Lesterbor ******END OF FILE******/

编码器的定时获取我放在了CubeMx生成的默认线程中一下是代码,当然啊,这个速度变量Speed是entern到这个文件中的,在MotorBoard_Encoder.c中我定义了一下
不知道for里面的这个三目运算符能不能看懂,在实际调试过程中发现,电机正转的时候计数值确实是向上计数的,但是反转的时候计数值是向下计数的,比如我电机现在的转速是+30 计数值就是30,但是你的电机转速是-30你的计数值就是8162也就是8192-30,所以我们在这加了一个判断语句,当计数值大于我溢出值的一半时我就减,当然我在MotorBoard_Encoder.c文件中定义了一个函数就是获取当前电机的旋转方向的,你也可以根据那个来判断是不是需要减掉计数值,嗯就是这样,如果你感觉你的电机速度值太小了,你可以稍微把采样时间调的大一点。每次取过计数值之后你需要吧计数器清空,也就是程序中的MotorBoard_Encoder_SetZero();这条语句,具体的定义在MotorBoard_Encoder.c文件中。
FreeRTOS.c

/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used 
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
	MotorBoard_Encoder_Init();
	
  for(;;)
  {
		Speed = MotorBoard_Encoder_GetSpeed()>4096?(MotorBoard_Encoder_GetSpeed()-8192):MotorBoard_Encoder_GetSpeed();
		MotorBoard_Encoder_SetZero();
    osDelay(30);
  }
  /* USER CODE END StartDefaultTask */
}

然后就是PID部分了,这部分是参考了网络自己写了一下,也加了一个低通滤波
MotorBoard_PID.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : MotorBoard_PID.c
  * @brief          : MotorBoard_PID program body
  * @author         : Lesterbor
  *	@time			: 2021-09-14	
  ******************************************************************************
  * @attention		: 包含低通滤波
  *
  *
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

	#include "MotorBoard_PID.h"
	#include "stdio.h"
	
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PT */

	pid_t 		PID_Motor;
	lowpass_t	LWF_Motor;
	float ControlSpeed = 0;
	
/* USER CODE END PT */

/* Function definition -------------------------------------------------------*/
/* USER CODE BEGIN FD */
/**
  * @Function name  : MotorBoard_CAN_Init
  * @Introduce  	: 编码电机的PID调节初始化
  * @Return 		: Null
  */
	void MotorBoard_PID_Init(float Kp,float Ki,float Kd){
		PID_Motor.SetValue= 0.0;			//设定的转速目标值
		PID_Motor.ActualValue= 0.0;			//实际转速值
		PID_Motor.err= 0.0;					//当前实际转速值与理想转速值的偏差
		PID_Motor.err_last=0.0;				//上一次的偏差
		PID_Motor.integral= 0.0;			//积分值
		PID_Motor.Kp= Kp;					//比例系数
		PID_Motor.Ki= Ki;					//积分系数
		PID_Motor.Kd= Kd;					//微分系数
	}
	
/**
  * @Function name  : MotorBoard_PID_realize
  * @Introduce  	: 编码电机的PID调节
  * @Return 		: 调节结果值
  */
	float MotorBoard_PID_Realize( float Target, float Input){
			PID_Motor.SetValue = Target;														//目标值传入
			PID_Motor.ActualValue = Input;											//实际值传入
			PID_Motor.err = PID_Motor.SetValue - PID_Motor.ActualValue;	//计算偏差
			PID_Motor.integral += PID_Motor.err;											//积分求和
			PID_Motor.result = PID_Motor.Kp * PID_Motor.err + PID_Motor.Ki * PID_Motor.integral + PID_Motor.Kd * ( PID_Motor.err - PID_Motor.err_last);//位置式公式
			PID_Motor.err_last = PID_Motor.err;												//留住上一次误差
			return PID_Motor.result;
	}

/**
  * @Function name  : MotorBoard_LWF_Init
  * @Introduce  	: 一阶低通滤波初始化
  * @Return 		: NULL
  */
	void MotorBoard_LWF_Init(void){
		LWF_Motor.Result_Last = 0;
	}

/**
  * @Function name  : MotorBoard_LWF_Realize
  * @Introduce  	: 一阶低通滤波控制
  * @Return 		: 本次滤波结果=(1-a)*本次采样值+a*上次滤波结果
  *				  	  滞后程度取决于a值的大小
  *				 	  a float型变量 取值范围为0-1
  */
	signed short MotorBoard_LWF_Realize(signed short Value,float a){
		LWF_Motor.Result= (1-a)*Value + a*LWF_Motor.Result_Last;
		LWF_Motor.Result_Last = LWF_Motor.Result;
		return LWF_Motor.Result;
	}
/* USER CODE END FD */
/************************ (C) CopyRight Lesterbor ******END OF FILE******/

MotorBoard_PID.h

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : MotorBoard_PID.h
  * @brief          : Header for MotorBoard_PID.c file.
  *                   This file provides code for the configuration
  *                   of the MotorBoard_PID instances
  * @author         : Lesterbor
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MOTORBOARD_PID_H_
#define __MOTORBOARD_PID_H_

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

  #include "main.h"
	 
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PT */
	//PID调节变量定义
	typedef struct{
		float SetValue;						//定义设定值
		float ActualValue;					//定义实际值
		float err;							//定义偏差值
		float err_last;						//定义上一个偏差值
		float Kp,Ki,Kd;						//定义比例、积分、微分系数
		float result;						//pid计算结果
		float voltage;						//定义转速值(控制执行器的变量)
		float integral;						//定义积分值
	}pid_t; 
	//一阶低通滤波变量定义
	typedef struct
	{
		signed short 	ActualValue;		//定义实际值
		float	 				a;			//滞后程度
		signed short  Result_Last;			//定义上一个结果
		signed short  Result;				//滤波计算结果
	}lowpass_t; 
	
/* USER CODE END PT */
/* Exported functions prototypes ---------------------------------------------*/
/* USER CODE BEGIN EFP */

	void 	MotorBoard_PID_Init(float Kp,float Ki,float Kd);			//PID初始化
	float MotorBoard_PID_Realize(float v,float v_r);											//PID控制
	
	void MotorBoard_LWF_Init(void);																				//一阶低通滤波初始化
	signed short MotorBoard_LWF_Realize(signed short Value,float a);	//一阶低通滤波
/* USER CODE END EFP */

#endif /* __MOTORBOARD_PID_H_ */
/************************ (C) CopyRight Lesterbor ******END OF FILE******/

之后再创建主线程所有的业务代码都在这里,至于CAN嘛,等我先把PID调完再说吧,先就不放CAN的代码了,下面程序中的Debugger数组就是大佬写的调参器变量,当程序烧录进去之后,在上位机改变对应变量的值,程序内部自己会改变,而那个DebuggerLine则会在上位机上显示一条曲线。
MainThread.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : MainThread.c
  * @brief          : MainThread program body
  * @author         : Lesterbor
  *	@time			: 2021-09-14	
  ******************************************************************************
  * @attention
  *
  *
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

  #include "MainThread.h"
  #include "MotorBoard_CAN.h"
  #include "MotorBoard_PID.h"
  #include "MotorBoard_Encoder.h"
  #include "DebuggerThread.h"
  #include "tim.h"
	
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PT */

  extern signed short Speed;
  extern unsigned int ID;		//本机ID 来自于CAN
  extern signed short MotorBoard_CAN_Output;	//来自于CAN
  extern float ControlSpeed;

/* USER CODE END PT */

/* Function definition -------------------------------------------------------*/
/* USER CODE BEGIN FD */
/**
  * @Function name  MainTask
  * @Introduce  	主线程函数
  * @Return 		Null
  */
void MainTask(void *argument){
	unsigned char TxData[8] = {0};

	HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);

	for(;;){
		//MotorBoard_LWF_Realize在PID控制的C文件中 功能是低通滤波
		MotorBoard_PID_Init(Debugger[0],Debugger[1],Debugger[2]);
		ControlSpeed = MotorBoard_PID_Realize(Debugger[3],MotorBoard_LWF_Realize(Speed,Debugger[5]));
		DebuggerLine[0] = Speed;
		Debugger[4]		= Speed;
		if(ControlSpeed> 0){
			__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_2,ControlSpeed);
			__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_1,0);
		}else{
			__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_2,0);
			__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_1,-ControlSpeed);		
		}
		osDelay(5);
	}
}

	/**
  * @brief 	主控制线程初始化
  * @retval None
  */
void MainThread_Init(void){
  const osThreadAttr_t MainTask_attributes = {"MainTask",0,0,0,0,256,(osPriority_t) osPriorityNormal};
	osThreadNew(MainTask, NULL, &MainTask_attributes);//创建主线程	
}
/* USER CODE END FD */
/************************ (C) CopyRight Lesterbor ******END OF FILE******/

上面主线程函数中的MainThread_Init(void)需要在主函数中添加初始化一下,不然程序不会进入自己定义的这个线程
MainThread.h

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : MainThread.h
  * @brief          : Header for MainThread.c file.
  *                   This file provides code for the configuration
  *                   of the MainThread instances
  * @author         : Lesterbor
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAINTHREAD_H_
#define __MAINTHREAD_H_

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

  #include "main.h"
  #include "cmsis_os.h"
  #include "FreeRTOS.h"
  #include "task.h"	
	 
/* USER CODE END Includes */

	
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PT */



/* USER CODE END PT */

	
/* Exported functions prototypes ---------------------------------------------*/
/* USER CODE BEGIN EFP */

void MainThread_Init(void);
		
/* USER CODE END EFP */

	
#endif /* __MAINTHREAD_H_ */
/************************ (C) CopyRight Lesterbor ******END OF FILE******/

目前代码也就写到了这里,PID还没有调完,总是不太对,实在不行的话就移植一下大佬的PID程序,嗯,先调一调看吧

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于STM32F103C6T6的AB相霍尔编码电机的PID转速调节(CubeMx-HAL库)(未完成-持续更新) 的相关文章

  • ReentrantReadWriteLock

    一ReentrantReadWriteLock 是Lock的另一种实现方式 我们知道ReentrantLock是一个排他锁 同一时间只允许一个线程访问 而ReentrantReadWriteLock允许多个读线程同时访问 但不允许写线程和读

随机推荐