基于stm32f103实现电机的速度调控--pid算法实现速度环(开源)
创始人
2024-11-06 06:06:40
0

 一、PID算法介绍

PID算法,即比例-积分-微分控制算法,是一种广泛应用的控制策略。在自动控制系统中,PID控制器根据系统的输入(设定值)和输出(实际值)之间的偏差,通过比例、积分和微分三种运算方式,产生适当的控制信号,从而实现对被控对象的精确控制。

一、PID算法的基本原理

PID算法的核心思想是根据偏差的比例(P)、积分(I)和微分(D)来计算控制量。这三种运算方式在控制过程中起到了不同的作用:

  1. 比例控制(P):根据偏差的大小直接调整控制量,偏差越大,控制量调整幅度越大。比例控制能够迅速响应偏差的变化,但可能导致系统振荡。
  2. 积分控制(I):对偏差进行积分运算,以消除系统中的稳态误差。积分控制能够逐渐减小偏差,使系统趋于稳定,但可能导致系统响应速度变慢。
  3. 微分控制(D):根据偏差的变化率来调整控制量,具有预测偏差趋势的能力。微分控制能够提前调整控制量,以抑制系统振荡,提高系统的稳定性。

二、PID算法的应用场景

PID算法因其简单、实用、鲁棒性强的特点,在各个领域得到了广泛应用。以下是一些典型的应用场景:

  1. 工业自动化:PID控制器在工业自动化领域中具有重要地位,如温度控制、压力控制、流量控制等。
  2. 机器人控制:PID算法可用于机器人的运动控制,如轨迹跟踪、姿态调整等。
  3. 航空航天:PID控制器在航空航天领域也发挥着重要作用,如飞机姿态控制、卫星轨道控制等。

三、PID算法的优缺点

PID算法的优点主要体现在以下几个方面:

  1. 原理简单:PID算法基于基本的数学运算,易于理解和实现。
  2. 适应性强:PID算法能够适应各种非线性、时变和不确定性系统,具有较强的鲁棒性。
  3. 控制效果好:通过合理调整PID参数,可以实现较高的控制精度和稳定性。

然而,PID算法也存在一些缺点:

  1. 参数调整困难:PID算法的参数(比例系数、积分系数、微分系数)需要根据具体系统进行调整,调整过程可能较为繁琐。
  2. 可能存在超调现象:在某些情况下,PID控制器可能导致系统超调,即控制量过大或过小,影响系统稳定性。

四、基本原理及代码实现

        通过红外对射及码盘的联合搭配测得实际速度,再设定目标速度,pid算法根据目标速度及实际速度进行调控。

main.c

#include "stm32f10x.h"                  // Device header #include "Delay.h" #include "OLED.h" #include "CountSensor.h" #include "mortpwm.h" #include "Motor.h" #include "usart.h" #include "Timer.h" float num; extern int CountSensor_Count; float speed; // PID控制器结构体   typedef struct {       float Kp;     // 比例系数       float Ki;     // 积分系数       float Kd;     // 微分系数       float SetPoint; // 目标值       float ProcessVariable; // 过程变量(当前速度)       float ErrorSum; // 误差和       float LastError; // 上一次误差       float Output; // PID输出   } PIDController; // 初始化PID控制器的函数   void PID_Init(PIDController *pid, float kp, float ki, float kd, float setPoint) {       pid->Kp = kp;             // 设置比例系数       pid->Ki = ki;             // 设置积分系数       pid->Kd = kd;             // 设置微分系数       pid->SetPoint = setPoint; // 设置目标值       pid->ProcessVariable = 0.0f; // 初始化过程变量       pid->ErrorSum = 0.0f;     // 初始化误差累积和       pid->LastError = 0.0f;     // 初始化上一次误差       pid->Output = 0.0f;       // 初始化输出值   }  // 计算PID控制器输出的函数   float PID_Calculate(PIDController *pid, float processVariable) {       float error = pid->SetPoint - processVariable; // 计算当前误差       pid->ErrorSum += error;                       // 更新误差累积和       float derivative = error - pid->LastError;    // 计算误差的微分       pid->Output = pid->Kp * error                 // 比例项                      + pid->Ki * pid->ErrorSum       // 积分项                      + pid->Kd * derivative;         // 微分项       pid->LastError = error;                       // 更新上一次误差       pid->ProcessVariable = processVariable;       // 更新过程变量       return pid->Output;                           // 返回PID输出值   }  int main(void) { 	/*模块初始化*/ 	OLED_Init();			//OLED初始化 	CountSensor_Init();		//计数传感器初始化 	Motor_Init();	 	mortpwm_Init(); 	Timer_Init(); 	uart_init(9600); 	/*显示静态字符串*/ 	OLED_ShowString(1, 1, "Count:");	//1行1列显示字符串Count: 	static PIDController speedPID;  // 定义并初始化PID控制器  	static int initialized = 0; // 用于判断PID是否已初始化   	while (1) 	{ 		if (!initialized) {   				PID_Init(&speedPID, 0.2f, 0.01f, 0.001f, 2.5f); // 初始化PID,目标速度为2.5  				initialized = 1;   		} 		 		// 计算PID输出   		float pwmValue = PID_Calculate(&speedPID, speed); 		OLED_ShowNum(1, 7, CountSensor_Get(), 5);		//OLED不断刷新显示CountSensor_Get的返回值 				// 设置电机PWM值   //		printf("%.3f\r\n",pwmValue); 		Goahead(pwmValue*10); 	} } /**   * 函    数:TIM3中断函数   * 参    数:无   * 返 回 值:无   * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行   *           函数名为预留的指定名称,可以从启动文件复制   *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入   */ void TIM3_IRQHandler(void) {  	if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)		//判断是否是TIM2的更新事件触发的中断 	{ 		num=CountSensor_Get();		//Num变量获取20孔码盘所经过的孔数 		speed=num/20.000;	 //每秒获取到的num数除以20为圈数,speed为每秒多少圈 		printf("每秒%.3f\r\n",speed);//串口打印输出速度 		CountSensor_Count=0; 		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);			//清除TIM2更新事件的中断标志位 															//中断标志位必须清除 															//否则中断将连续不断地触发,导致主程序卡死  	} }  

电机pwm控制转速

mortpwm.c

#include "stm32f10x.h"                  // Device header #include "stm32f10x_tim.h"     #include "mortpwm.h"    void mortpwm_Init(void) { 	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); 	 //	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//用来打开AFIO外设的时钟。AFIO外设用于控制引脚重映射功能。引脚重映射功能允许将某些外设的引脚映射到其他引脚上,以便更灵活地配置引脚功能。 //	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);//将TIM2定时器的引脚部分重映射到其他引脚上 //	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//禁用JTAG调试接口。在某些情况下,需要释放JTAG接口的引脚以供其他功能使用,这时就需要禁用JTAG调试接口。 	 GPIO_InitTypeDef GPIO_InitStructure;  // 定义一个GPIO_InitTypeDef类型的变量GPIO_InitStructure //GPIO_InitTypeDef GPIO_InitStructure2;  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 设置GPIO_InitStructure的GPIO模式为复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;  // 设置GPIO_InitStructure的引脚为GPIOA的第0号引脚 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // 设置GPIO_InitStructure的输出速度为50MHz GPIO_Init(GPIOA, &GPIO_InitStructure);  // 初始化GPIOA的引脚配置为GPIO_InitStructure所定义的配置  //GPIO_InitStructure2.GPIO_Mode = GPIO_Mode_AF_PP;  // 设置GPIO_InitStructure的GPIO模式为复用推挽输出 //GPIO_InitStructure2.GPIO_Pin = GPIO_Pin_6;  // 设置GPIO_InitStructure的引脚为GPIOA的第0号引脚 //GPIO_InitStructure2.GPIO_Speed = GPIO_Speed_50MHz;  // 设置GPIO_InitStructure的输出速度为50MHz //GPIO_Init(GPIOA, &GPIO_InitStructure2);  // 初始化GPIOA的引脚配置为GPIO_InitStructure所定义的配置	  TIM_InternalClockConfig(TIM2);  // 配置TIM2的内部时钟 //TIM_InternalClockConfig(TIM3);  // 配置TIM2的内部时钟  TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;  // 定义一个TIM_TimeBaseInitTypeDef类型的变量TIM_TimeBaseInitStructure //TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure2;  TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  // 设置时钟分频为不分频 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 设置计数模式为向上计数 TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;  // 设置周期为100-1,即ARR寄存器的值 TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;  // 设置预分频器的值为720-1,即PSC寄存器的值 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;  // 设置重复计数器的值为0 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);  // 初始化TIM2的时间基础设置为TIM_TimeBaseInitStructure所定义的配置  //TIM_TimeBaseInitStructure2.TIM_ClockDivision = TIM_CKD_DIV1;  // 设置时钟分频为不分频 //TIM_TimeBaseInitStructure2.TIM_CounterMode = TIM_CounterMode_Up;  // 设置计数模式为向上计数 //TIM_TimeBaseInitStructure2.TIM_Period = 100 - 1;  // 设置周期为100-1,即ARR寄存器的值 //TIM_TimeBaseInitStructure2.TIM_Prescaler = 720 - 1;  // 设置预分频器的值为720-1,即PSC寄存器的值 //TIM_TimeBaseInitStructure2.TIM_RepetitionCounter = 0;  // 设置重复计数器的值为0 //TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure2);  // 初始化TIM2的时间基础设置为TIM_TimeBaseInitStructure所定义的配置   TIM_OCInitTypeDef TIM_OCInitStructure;  // 定义一个TIM_OCInitTypeDef类型的变量TIM_OCInitStructure TIM_OCStructInit(&TIM_OCInitStructure);  // 初始化TIM_OCInitStructure为默认值  //TIM_OCInitTypeDef TIM_OCInitStructure2;  // 定义一个TIM_OCInitTypeDef类型的变量TIM_OCInitStructure //TIM_OCStructInit(&TIM_OCInitStructure2);  // 初始化TIM_OCInitStructure为默认值  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  // 设置输出比较模式为PWM模式1 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  // 设置输出极性为高电平有效 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  // 启用输出 TIM_OCInitStructure.TIM_Pulse = 0;  // 设置脉冲值为0,即CCR寄存器的值,值越大,LED越暗 TIM_OC1Init(TIM2, &TIM_OCInitStructure);  // 初始化TIM2的输出比较通道1为TIM_OCInitStructure所定义的配置 TIM_OC2Init(TIM2, &TIM_OCInitStructure);  // 初始化TIM2的输出比较通道1为TIM_OCInitStructure所定义的配置 TIM_Cmd(TIM2, ENABLE);  // 使能TIM2定时器   //TIM_OCInitStructure2.TIM_OCMode = TIM_OCMode_PWM1;  // 设置输出比较模式为PWM模式1 //TIM_OCInitStructure2.TIM_OCPolarity = TIM_OCPolarity_High;  // 设置输出极性为高电平有效 //TIM_OCInitStructure2.TIM_OutputState = TIM_OutputState_Enable;  // 启用输出 //TIM_OCInitStructure2.TIM_Pulse = 0;  // 设置脉冲值为0,即CCR寄存器的值,值越大,LED越暗 //TIM_OC1Init(TIM3, &TIM_OCInitStructure2);  // 初始化TIM2的输出比较通道1为TIM_OCInitStructure所定义的配置  //TIM_Cmd(TIM3, ENABLE);  // 使能TIM2定时器 }  void MORT_SetCompare1(uint16_t Compare)  //设置通道1的ccr的值 { 	TIM_SetCompare1(TIM2, Compare); 	 } void MORT_SetCompare2(uint16_t Compare)  //设置通道1的ccr的值 { 	TIM_SetCompare2(TIM2, Compare); 	 } 

mortpwm.h

#ifndef __TIMER_H #define __TIMER_H #include "stdint.h" void mortpwm_Init(void); void MORT_SetCompare1(uint16_t Compare); void MORT_SetCompare2(uint16_t Compare); #endif  

五、测速结果

经过一定时间速度达到2.5圈每秒,可给电机添加一定阻力减缓运转速度,通过pid调控后会加大pwm输出,从而调控其速度再次达到所设定值。

f9462f8555454908bd7006535d90f1b4.png

 

关于测速篇可看: 

测c基于stm32F103实现MH-Sensor红外对射模块加测速码盘进行测速

 

 源代码:链接: 通过百度网盘分享的文件:对射式红外传感器…
链接:https://pan.baidu.com/s/1bN8mKGs-qY6nfrjemnctpA?pwd=nxmx 
提取码:nxmx
复制这段内容打开「百度网盘APP 即可获取」

 

 

相关内容

热门资讯

透视计算(wpK)wpk透视辅... 透视计算(wpK)wpk透视辅助工具(透视)详细辅助实用技巧(竟然真的有挂);1、这是跨平台的wpk...
透视软件(aapOker)aa... 透视软件(aapOker)aapoker发牌机制(透视)本来真的是有挂(详细辅助揭秘教程)1、操作简...
透视教程!智星德州菠萝开挂,(... 透视教程!智星德州菠萝开挂,(德州wepower)一直真的是有挂(详细辅助大神讲解)1、智星德州菠萝...
透视科技(wpK)wpk有辅助... 透视科技(wpK)wpk有辅助挂(透视)详细辅助透视教程(果然真的有挂);1、超多福利:超高返利,海...
透视免费(aApoker)aa... 透视免费(aApoker)aapoker外挂(透视)一直真的有挂(详细辅助细节揭秘)1、完成aapo...
透视科技!德州之星辅助,(线上... 透视科技!德州之星辅助,(线上wpk德州)果然是真的有挂(详细辅助微扑克教程)在进入德州之星辅助辅助...
透视了解(wPk)微扑克德州专... 透视了解(wPk)微扑克德州专用辅助器(透视)详细辅助攻略教程(一直真的是有挂)1、该软件可以轻松地...
透视规律(aapoKer)aa... 透视规律(aapoKer)aapoker挂(透视)其实是有挂(详细辅助新2025版);在进入aapo...
透视辅助!德扑数据软件,(德州... 透视辅助!德扑数据软件,(德州)都是有挂(详细辅助普及教程)亲,关键说明,德扑数据软件赛季回归,德扑...
透视脚本(wpK)wpk有外挂... 透视脚本(wpK)wpk有外挂(透视)详细辅助透明挂教程(其实是有挂)1、wpk有外挂ai机器人多个...