概述
基于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程序,嗯,先调一调看吧
最后
以上就是淡然项链为你收集整理的基于STM32F103C6T6的AB相霍尔编码电机的PID转速调节(CubeMx-HAL库)(未完成-持续更新)的全部内容,希望文章能够帮你解决基于STM32F103C6T6的AB相霍尔编码电机的PID转速调节(CubeMx-HAL库)(未完成-持续更新)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复