HAL库直流有刷电机的PWM驱动以及PID控制算法的实现

本文介绍了使用STM32f103c8t6单片机控制平衡车电机,通过TB6612驱动模块和PWM信号实现电机控制,同时探讨了PID控制算法在电机速度调整中的应用,包括系统时钟配置和GPIO/TIM的使用。
摘要由CSDN通过智能技术生成

  本个STM32平衡车项目采用STM32f103c8t6,电机驱动采用的是带稳压的TB6612电机驱动模块,电机采用的是直流霍尔编码的有刷电机。

  首先是需要明确硬件接线原理,STM32f103c8t6的端口输出PWM-->驱动TB6612-->驱动电机转动。

12v电源-->降压模块和电机供电-->输出3.3v和5v,3.3v给STM32f103c8t6芯片供电,同时输出5v给TB6612板载供电。

  首先我们需要知道TB6612的驱动方法,下图为他的

  .目前就单一个电机驱动来说,要想让电机驱动起来,就需要满足以下条件:16引脚PWMA有PWM波形输入、AIN1和AIN2有控制电平的输入、STBY有5v的板载供电。其中AIN1=1 AIN2=0,电机正转/AIN1=0 AIN2=1,电机反转/AIN1=0 AIN2=0,电机不转。所以驱动电机A的接线图如下:

  如果需要控制电机B的话也只需要和控制A一样输入PWMB和BIN1、BIN2即可,输出为BO1和BO2。但是板载供电STBY无论如何都要5v供电的哟!!!

接下来就的主要问题就是如何利用STM32f103c8t6输出PWM给电机驱动板啦!!

  老样子我们还是利用STM32CubeMX对STM32f103c8t6进行开发。首先需要打开STM32CubeMX软件选择stm32f103c8tx:

然后配置常规的RCC和SYS

 然后我们需要配置哪个GPIO来输出PWM,具体的GPIO需要看你工程上引脚的安排来配置,我们这就随意配置一个PA1引脚吧!!点开PA1引脚看看他的引脚是与哪个TIM的哪个通道相连的(具体也可以查看芯片手册!!!!)

 可以看到PA1是与TIM2的通道2相连的,那我们就选中TIM_CH2,然后就去配置TIM2。

然后我们就需要去配置时钟树:

这里的原理是需要琢磨的,主要是控制芯片的工作频率以及其TIM2的工作频率:

其工作原理如下:定时器工作频率=外设总线频率/(PSC+1)【PSC:预分频器】

定时频率=定时器工作频率/(CNT+1)【CNT:自动重转载值】

PWM的工作模式等可以去官方资料的芯片手册中可以查到,这里就不多赘述啦!!!生成工程后我们需要手动的打开TIM2的通道2输出PWM。

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
 uint16_t pwmVal=500;   //定义PWM的占空比50%!!!
  
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);    //打开定时器2的通道2
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,pwmVal);  //设置定时器2通道2PWM输出的占空比
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

  然后烧录到板子上就可以实现PA1引脚的PWM输出了!!对其上诉的硬件接线原理就可以发现电机开始被驱动了。

在这采用PWM控制呼吸灯的原理对电机进行驱动,可以发现电机先启动,一会后又开始制动:代码如下:

#include "main.h"
#include "tim.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
 uint16_t pwmVal=0;
  
/* USER CODE END Includes */

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

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_3);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
while(pwmVal<500)
{
	pwmVal ++;
	__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,pwmVal);
	HAL_Delay(50);
}
while(pwmVal)
{
	pwmVal --;
	__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,pwmVal);
	HAL_Delay(50);
}
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

就可以发现电机匀速启动然后匀速制动,如果需要调节启动和制动的时间则调节while循环里的HAL_Delay即可。

接下来我们简单的来说说电机的PID控制算法。 

  PID控制应该算是应用非常广泛的控制算法了。小到控制一个元件的温度,大到控制无人机的飞行姿态和飞行速度等等,都可以使用PID控制。这里我们从原理上来理解PID控制。PID(proportion integration differentiation)其实就是指比例,积分,微分控制。PID的公式和原理推导我就不多赘述了,大伙有兴趣的话可以去看看其他大佬们写的原理,这里只简单的说说给个参数的作用和如何调节三个参数!!!(重点)

P(比例系数):成比例的反应控制系统中输入与输出的偏差信号,只要偏差一旦产生,就立刻产生控制的作用来减少产生的误差。

I(Ti积分系数):在比例控制环节产生了静态误差,在积分环节中,主要作用就是消除静态误差来提高系统的稳定性。

D(Td微分系数):微分环节是反应系统偏差的一个变化趋势,可以在误差来临前提前引入一个有效的修正信号。

而常用的PID又分位置式PID和增量式PID:

位置式PID是一种非递推式算法,通过不断累加,可以直接控制执行机构(平衡小车),u(k)的值和执行机构的实际位置(如小车当前角度)是一一对应的,因此在执行机构中不带积分不见的对象中可以很好的应用。

增量式不需要不断累加,控制的增量△u(k)也只与最近3次的采样值有关。在速度闭环控制中有很好的实时性。

在进行PID控制时,位置式PID需要又积分限幅和输出限幅,而增量式PID只需要输出限幅。

重要的是PID参数的整定

这里我给大家推荐一个我常用的方法:一般调节法!!

一般调节法:在输出不振荡时,增大比例增益P。在输出不振荡时,减少积分常数Ti。

在输出不震荡时,增大微分时间常数Td。

1:确认比例增益P时,首先去掉PID中的积分Ti和微分Td,一般让Ti=0和Td=0,让其PID变为纯比例P调节。输入设定为系统允许的最大值的60%~70%,由0逐渐增大增益P,直到系统出现振荡;然后再反过来,逐渐将p减小,直至振荡消失,记录此时的比例增益P,设定为PID的比例增益P为当前的60%~70%,。

2:确定积分时间常数Ti:设定一个较大的积分时间常数Ti的初值,然后逐渐减小Ti,直至系统出现振荡,在翻过来逐渐加大Ti,直至系统振荡小时。记录此时的Ti,设定PID积分时间常数Ti为当前的150%~180%。

3:确认微分时间常数Td:积分时间常数Td一般不用设定,若要设定则与P和Ti方式相同,取不震荡的30%。

PID理想输出的时候的间两个波,前后高低4:1!!!

说到了PID,我们就需要知道他是一个闭环的控制系统,而我们需要经过采样才能知道电机的转速等信息,采样不可能是连续采样的(离散系统),这就要涉及到采样周期的选择了,

采样周期的选择:采样周期的选则是个矛盾的问题,采样周期越短控制效果越接近于连续,对于大多算法缩短采样周期可以使得控制回路的性能得到改善,但是采样周期的缩短也会增加和占用更多CPU的计算工作量,增加运算的负担,而且有些变化缓慢的受控对象也无需很高的采样频率,过高的采样周期会让CPU的资源浪费,变得没有意义,因此如何选着采样周期至关重要。从这就需要介绍一个著名的定理:香农采样定理(Nyquist——Shannon采样定理)。

在设计离散系统时,香农采样定理是必须严格遵守的一条准则,以为他指明了从采样信号中不失真的恢复原连续信号所需的理论上的最小采样周期T。香农定理指出:如果采样器的输入信号e(t)具有有限带宽,并且有直到ωn的频率分量,则使信号e(t)圆满地从采样信号e*(t)中恢复过来的采样周期T,要满足条件:,也就是

在控制工程实践中,一般总取,而不取恰好等于。所以采样周期要小于整数周期的1/2,采样频率应该要大于原始频率的2倍。

数字PID控制代码的实现:通过板级支持包实现需要创建一个bsp_pid.c和bsp_pid.h来实现。

在bsp_pid.h中我们定义一下PID的结构体:

#ifndef __BSP_PID_H
#define	__BSP_PID_H   //预定义,如果没有定义__BSP_PID_H文件,那就定义__BSP_PID_H

typedef struct
{
    float target_val;           //目标值
    float actual_val;        		//实际值
    float err;             			//定义偏差值
    float err_last;          		//定义上一个偏差值
    float Kp,Ki,Kd;          		//定义比例、积分、微分系数
    float integral;          		//定义积分值
}_pid;

#endif   //预定义结束(预定义标准格式!!!)

 然后我们在bsp_pid.c实现一些PID算法:


//定义全局变量

_pid pid;

void PID_param_init()       //初始化参数
{
		/* 初始化参数 */
//    printf("PID_init begin \n");
    pid.target_val=0.0;				
    pid.actual_val=0.0;
    pid.err=0.0;
    pid.err_last=0.0;
    pid.integral=0.0;

		pid.Kp = 0.01;      //这里PID的三个参数的值可以在这初始化!!
		pid.Ki = 0.80;
		pid.Kd = 0.04;

}


void set_pid_target(float temp_val)
{
  pid.target_val = temp_val;    // 设置当前的目标值
}


float get_pid_target(void)
{
  return pid.target_val;    // 设置当前的目标值
}


void set_p_i_d(float p, float i, float d)
{
  	pid.Kp = p;    // 设置比例系数 P
		pid.Ki = i;    // 设置积分系数 I
		pid.Kd = d;    // 设置微分系数 D
}

float PID_realize(float temp_val)
{
	/*计算目标值与实际值的误差*/
    pid.err=pid.target_val-temp_val;
	/*误差累积*/
    pid.integral+=pid.err;
	/*PID算法实现*/
    pid.actual_val=pid.Kp*pid.err+pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
	/*误差传递*/
    pid.err_last=pid.err;
	/*返回当前实际值*/
    return pid.actual_val;
}


在主函数中我们需要初始化一下PID算法:    /* PID算法参数初始化 */  PID_param_init();    

这样我们一个简易的PID算法就构建完成了。再后面我们再来构建一下通过上位机来调试PID的3个参数,PID通常也需要和别的模块共同使用才能发挥其作用!!!

本片文章就到这里啦!!希望对大家有帮助,如果发现错误可以提出!!如果喜欢请点个赞。最后附上一张本次PWM和PID控制小实验的图吧!!!如果有兴趣的小伙伴可以私信我一起讨论和研究哟!!

  

  • 18
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
使用STM32CubeMX结合HAL库进行直流有刷电机PID控制的步骤如下: 1. 配置PWM输出和相关引脚: - 在STM32CubeMX中,选择适当的引脚作为PWM输出引脚,并配置为输出模式。 - 设置PWM的频率和占空比,以适应电机控制需求。 2. 编写代码: - 在HAL库中,使用相应的函数初始化PWM输出引脚。 - 设置PID控制所需的参数,如比例系数、积分系数和微分系数。 - 在主循环中,读取电机的实际位置反馈值。 - 根据PID算法计算出控制信号(电机的输出)。 - 将控制信号输出到PWM引脚上,以控制电机的转速或位置。 3. 对PID进行调整: - 可以通过修改PID参数来优化电机的响应速度和稳定性。 - 通过实验和调试,逐步调整比例系数、积分系数和微分系数,以达到预期的控制效果。 请注意,以上步骤仅提供了基本的框架和思路,具体的实现会根据具体的硬件和软件平台有所不同。具体的代码实现和调整参数的方法可以参考引用中提供的教程。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [stm32直流电机PID控制hal库(Cubemx)](https://blog.csdn.net/qq_59953808/article/details/128431703)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值