STM32F103RCT6双轮PID闭环小车(L298N)

本文围绕基于STM32的平衡车展开,介绍了电机驱动L298N、MPU6050陀螺仪、霍尔编码电机等硬件的使用方法和原理,还提及0.96寸OLED显示屏、HC06蓝牙模块的应用。此外,详细阐述了PID控制算法的组成和工作原理,为平衡车的开发提供了技术支持。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、电机驱动L298N

        L298N是一个常用的双H桥电机驱动器模块,用于控制直流电机或步进电机的方向和速度。它适用于许多电机控制应用,如机器人、小车、舵机等。

       l298n的使用方法有好几种,我所使用的是最简单的一种,使用方法如下:

        输出A: 通道A输出 ,连接1电机的正负
        输出B: 通道B输出 ,连接2电机正反
        12V供电: 主电源正极输入
        供电GND: 主电源正负极极输入
        5V输出: 5v电压输出端,可用于给MCU单片机供电
        ENA: 通道A使能 (插上跳线帽)
        ENB: 通道B使能 (插上跳线帽)
        IN1~IN4: 逻辑输入IN1 ~ IN2控制通道A,逻辑输入IN3~IN4控制通道B(分别接入4路PWM)
        板载5V跳线帽: 接上后板载5V输出有效

详细的L298N的使用方法

二、MPU6050陀螺仪

      MPU6050是一款常用的六轴陀螺仪和加速度计传感器模块,广泛用于嵌入式系统、机器人、飞行器、运动控制等应用。它集成了三轴陀螺仪和三轴加速度计,能够提供精确的姿态和运动数据,通常称为六轴运动陀螺仪。

        陀螺仪的三个角度:

                Pitch:俯仰角(抬头低头)
                Roll:滚转角(翻身)
                Yaw:偏航角(转弯)

        MPU6050是使用IIC通信协议与单片机进行通信,以此来获取陀螺仪的角度和角加速度数据。

VCC:电源正
GND:地
SCL: I2C串行时 钟线/SPI串行时钟端口
SDA: I2C串行 数据线/SPI串行数据输入
XDA:连接其他I2C设备的主机数据口(不需要接单片机)
XCL:给I2C设备提供主时钟(不需要接单片机)
ADO:I2C器件地址选择位,地址管脚(不需要接单片机)
INT:中断引脚(接单片机的引脚组成外部中断或者不接使用其他中断)

// MPU6050 IIC PB8 PB9
//初始化
void IIC_Init(void)
{			

  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口时钟
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;	//端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;      //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     //50M
  GPIO_Init(GPIOB, &GPIO_InitStructure);					      //根据设定参数初始化GPIOB 
	
}


//获取mpu6050角度值
void Get_Angle(void)
{ 
			Read_DMP();                      //===读取加速度、角速度、倾角
			Angle_Balance=Pitch;             //===更新平衡倾角
	         Angle_Turn=Yaw;
			Gyro_Balance=gyro[1];            //===更新平衡角速度
		    Gyro_Turn=gyro[2];                 //更新转向角速度
	
	      Acceleration_Z=accel[2];         //===更新Z轴加速度计
	Gyro_Z=(I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_ZOUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_ZOUT_L);    //读取Z轴陀螺仪
}

三、霍尔编码电机

        霍尔编码电机是一种具有霍尔传感器的电机,通常用于测量电机的转速和位置。霍尔传感器是一种用于检测磁场的传感器,可以用来测量电机转子的位置和速度。这种编码方式常用于电机控制和位置反馈系统中。

  1. 原理:霍尔编码电机通常集成了霍尔传感器,这些传感器可以检测电机的转子上的永磁磁极。通过检测磁场的变化,霍尔传感器可以确定电机转子的位置和速度。

  2. 位置反馈:霍尔编码电机提供了位置反馈,可以告诉你电机的当前位置。这对于需要精确控制电机的应用非常重要,如机器人、CNC机床和自动化系统。

  3. 速度反馈:除了位置反馈,霍尔编码电机还可以提供速度反馈。通过测量位置变化的速度,你可以获得电机的实时速度信息。

  4. 闭环控制:霍尔编码电机通常与闭环控制系统一起使用。闭环控制可以根据实际的位置和速度反馈调整电机的控制信号,以实现精确的位置和速度控制。

  5. 编码方式:霍尔编码电机通常使用不同的霍尔编码方式,如单通道霍尔编码、双通道霍尔编码和四通道霍尔编码。这些编码方式提供不同的精度和分辨率。

  6. 应用:霍尔编码电机广泛用于需要精确控制的应用,包括工业自动化、机器人、医疗设备、自动门、摄像机云台等

引脚说明:

1、M1电机动力线1 ( 12V)(接l298n的输出端)
2、GND编码器电源-
3、C1编码器A相(接单片机定时器捕获引脚)
4、C2编码器B相(接单片机定时器捕获引脚)
5、3V3编码器电源+ ( 5V)
6、M2电机动力线2 ( 12V )(接l298n的输出端)

 ①定时器捕获编码器脉冲:

#include "encoder.h"
#include "stm32f10x_gpio.h"
 
 //TIM3 CH1 PA6 
 //TIM3 CH2 PA7
void Encoder_Init_TIM3(void)  
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
  TIM_ICInitTypeDef TIM_ICInitStructure;  
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//使能定时器4的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PB端口时钟
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;	//端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);					      //根据设定参数初始化GPIOB
  
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  TIM_TimeBaseStructure.TIM_Prescaler = 0x00; // 预分频器 
  TIM_TimeBaseStructure.TIM_Period = 65535; //设定计数器自动重装值
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上计数  
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
  TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3
  TIM_ICStructInit(&TIM_ICInitStructure);
  TIM_ICInitStructure.TIM_ICFilter = 10;
  TIM_ICInit(TIM3, &TIM_ICInitStructure);
  TIM_ClearFlag(TIM3, TIM_FLAG_Update);//清除TIM的更新标志位
  TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
  //Reset counter
  TIM_SetCounter(TIM3,0);
  TIM_Cmd(TIM3, ENABLE); 
}

 //TIM4 CH1 PB6 
 //TIM4 CH2 PB7

void Encoder_Init_TIM4(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
  TIM_ICInitTypeDef TIM_ICInitStructure;  
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//使能定时器4的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能PB端口时钟
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;	//端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
  GPIO_Init(GPIOB, &GPIO_InitStructure);					      //根据设定参数初始化GPIOB
  
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器 
  TIM_TimeBaseStructure.TIM_Period = 65535; //设定计数器自动重装值
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上计数  
  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
  TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3
  TIM_ICStructInit(&TIM_ICInitStructure);
  TIM_ICInitStructure.TIM_ICFilter = 10;
  TIM_ICInit(TIM4, &TIM_ICInitStructure);
  TIM_ClearFlag(TIM4, TIM_FLAG_Update);//清除TIM的更新标志位
  TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
  //Reset counter
  TIM_SetCounter(TIM4,0);
  TIM_Cmd(TIM4, ENABLE); 
}



int Read_Encoder(u8 TIMX)
{
    int Encoder_TIM;    
   switch(TIMX)
	 {
	   case 3:  Encoder_TIM= (short)TIM3 -> CNT;  TIM3 -> CNT=0;break;
	   case 4:  Encoder_TIM= (short)TIM4 -> CNT;  TIM4 -> CNT=0;break;	
	   default: Encoder_TIM=0;
	 }
		return Encoder_TIM;
}



void TIM4_IRQHandler(void)
{ 		    		  			    
	if(TIM4->SR&0X0001)//溢出中断
	{    				   				     	    	
	}				   
	TIM4->SR&=~(1<<0);//清除中断标志位 	    
}


void TIM3_IRQHandler(void)
{ 		    		  			    
	if(TIM3->SR&0X0001)//溢出中断
	{    				   				     	    	
	}				   
	TIM3->SR&=~(1<<0);//清除中断标志位 	    
}


②定时器PWM的产生(定时器TIM8是高级定时器,配置时需要注意)

#include "motor.h"
// TIM5 PA2 PA3
// TIM8 PC6 PC7

void TIM5_PWM_Init(u16 arr,u16 psc){

	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_OCInitTypeDef TIM_OCInitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); 
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3; //TIM5_CH3 //TIM_CH4
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	TIM_TimeBaseStructure.TIM_Period=arr;
	TIM_TimeBaseStructure.TIM_Prescaler=psc;
	TIM_TimeBaseStructure.TIM_ClockDivision=0;
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
	
	TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
	
	TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse=0;
	TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
	
	TIM_OC3Init(TIM5, &TIM_OCInitStructure);  
	TIM_OC4Init(TIM5, &TIM_OCInitStructure);  
	
	TIM_CtrlPWMOutputs(TIM5,ENABLE);	//MOE 主输出使能	

	TIM_OC3PreloadConfig(TIM5, TIM_OCPreload_Enable);  //CH3预装载使能	 
	TIM_OC4PreloadConfig(TIM5, TIM_OCPreload_Enable);  //CH4预装载使能	 
	
	TIM_ARRPreloadConfig(TIM5, ENABLE); //使能TIM5在ARR上的预装载寄存器
	
	TIM_Cmd(TIM5, ENABLE);  //使能TIM5
	
}
void TIM8_PWM_Init(u16 arr,u16 psc){
	
    GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef TIM_OCInitstruct;
	
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_TIM8 ,ENABLE);
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE); //时钟使能

	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOC,&GPIO_InitStructure);
	
	TIM_TimeBaseStructure.TIM_Period = arr; 
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 
	TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1; 
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
	TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure); 
 
	TIM_OCInitstruct.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitstruct.TIM_OCNPolarity=TIM_OCNPolarity_High;
	TIM_OCInitstruct.TIM_OCPolarity=TIM_OCPolarity_High;
	TIM_OCInitstruct.TIM_OutputState=TIM_OutputState_Enable;
	TIM_OCInitstruct.TIM_OCIdleState = TIM_OCIdleState_Set;
	TIM_OCInitstruct.TIM_OCNIdleState=TIM_OCNIdleState_Reset;

	TIM_OC1Init(TIM8,&TIM_OCInitstruct);
	TIM_OC2Init(TIM8,&TIM_OCInitstruct);
	
	TIM_OC1PreloadConfig(TIM8,TIM_OCPreload_Enable);
	TIM_OC2PreloadConfig(TIM8,TIM_OCPreload_Enable);
	
	TIM_CtrlPWMOutputs(TIM8,ENABLE);	//MOE 主输出使能	 
	
	TIM_ARRPreloadConfig(TIM8, ENABLE); //使能TIM3在ARR上的预装载寄存器
	
	TIM_Cmd(TIM8, ENABLE);  //使能TIM3
	TIM_CtrlPWMOutputs(TIM8, ENABLE);// 主输出使能,当使用的是通用定时器时,这句不需要 s
	
}

四、0.96寸OLED的使用(增加人机交互,可更好实时观察数据)

        0.96寸OLED(Organic Light Emitting Diode)显示屏是一种小尺寸的有机发光二极管显示屏,通常用于嵌入式系统、小型电子设备和Arduino项目中。它提供了高亮度、高对比度、低功耗以及较宽的视角,非常适合小型信息显示和用户界面设计。

        OLED通常有2种,第一种是IIC通讯,第二种是SPI通讯。IIC协议更加简单,使用起来方便,但是性能没有SPI强,但是在OLED上使用IIC是完全足够了。这里就不详细讲解IIC和SPI的区别了,有兴趣的同学可以去下面的网站看看大佬写的文章。

        平衡车使用OLED的主要目的是用来观察一下数据,比如:编码器的数据、陀螺仪角度数据、使用按键调PID参数时的参数数据等等。

IIC和SPI简介

//初始化OLED				    
void OLED_Init(void)
{ 	
	
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOC, ENABLE );	//使能GPIOC时钟
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	GPIO_SetBits(GPIOC,GPIO_Pin_12|GPIO_Pin_11); 	 
	
	delay_ms(200);

	OLED_WR_Byte(0xAE,OLED_CMD);//--display off
	OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
	OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
	OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  
	OLED_WR_Byte(0xB0,OLED_CMD);//--set page address
	OLED_WR_Byte(0x81,OLED_CMD); // contract control
	OLED_WR_Byte(0xFF,OLED_CMD);//--128   
	OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap 
	OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse
	OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
	OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 duty
	OLED_WR_Byte(0xC8,OLED_CMD);//Com scan direction
	OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset
	OLED_WR_Byte(0x00,OLED_CMD);//
	
	OLED_WR_Byte(0xD5,OLED_CMD);//set osc division
	OLED_WR_Byte(0x80,OLED_CMD);//
	
	OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off
	OLED_WR_Byte(0x05,OLED_CMD);//
	
	OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period
	OLED_WR_Byte(0xF1,OLED_CMD);//
	
	OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion
	OLED_WR_Byte(0x12,OLED_CMD);//
	
	OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh
	OLED_WR_Byte(0x30,OLED_CMD);//
	
	OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable
	OLED_WR_Byte(0x14,OLED_CMD);//
	
	OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
}  

五、HC06蓝牙模块

        HC-06是一种常见的蓝牙串口模块,通常被用于将串口设备(如Arduino或其他微控制器)与蓝牙无线通信连接起来。以下是一些关于HC-06蓝牙模块的重要信息:

  1. 功能:HC-06蓝牙模块是一个串口蓝牙模块,可通过UART串口与其他设备通信。它可以用作透明串口传输,将串口数据通过蓝牙连接传输到其他设备,也可以用于构建蓝牙串口通信的应用程序。

  2. 通信距离:通常,HC-06模块的通信距离在10米左右,但这取决于环境条件和障碍物。

  3. 配对:HC-06通常以主从设备的方式工作,其中一个设备(通常是手机或电脑)充当主机,而HC-06模块充当从设备。在首次使用时,通常需要将HC-06模块与主机设备进行蓝牙配对。

  4. 配置:HC-06模块通常有一个AT命令模式,您可以使用这些命令来配置模块的参数,如蓝牙名称、波特率等。要进入AT命令模式,通常需要将模块上的特定引脚连接到地,并重启模块。

  5. 电源:HC-06通常需要3.3V或5V电源供电,具体的电源要求可以查看模块的规格。

  6. 应用:HC-06模块广泛用于各种项目,例如蓝牙遥控器、传感器数据传输、蓝牙串口通信等。它是一个经济实惠的解决方案,用于在项目中添加蓝牙功能。

  7. 注意事项:使用HC-06模块时需要小心处理电源和引脚连接,以免损坏模块。还需要注意模块的默认配置,例如波特率和配对密码。

#include "hc06.h"


u8 res;  				  //设置全局变量
void HC06_Init(u16 arr)  
{  
    GPIO_InitTypeDef GPIO_InitStrue;  
    USART_InitTypeDef USART_InitStrue;  
    NVIC_InitTypeDef NVIC_InitStrue;  
      
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //GPIO端口使能   
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); //串口端口使能  
      
    GPIO_InitStrue.GPIO_Mode=GPIO_Mode_AF_PP;  
    GPIO_InitStrue.GPIO_Pin=GPIO_Pin_10;  
    GPIO_InitStrue.GPIO_Speed=GPIO_Speed_50MHz;  
    GPIO_Init(GPIOB,&GPIO_InitStrue);  
      
    GPIO_InitStrue.GPIO_Mode=GPIO_Mode_IN_FLOATING;  
    GPIO_InitStrue.GPIO_Pin=GPIO_Pin_11;  
    GPIO_InitStrue.GPIO_Speed=GPIO_Speed_50MHz;  
    GPIO_Init(GPIOB,&GPIO_InitStrue);  
      
    USART_InitStrue.USART_BaudRate=arr;  
    USART_InitStrue.USART_HardwareFlowControl=USART_HardwareFlowControl_None;  
    USART_InitStrue.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;  
    USART_InitStrue.USART_Parity=USART_Parity_No;  
    USART_InitStrue.USART_StopBits=USART_StopBits_1;  
    USART_InitStrue.USART_WordLength=USART_WordLength_8b;  
      
    USART_Init(USART3,&USART_InitStrue);
      
    USART_Cmd(USART3,ENABLE);					//ʹ�ܴ���2  
      
    USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);//���������ж�  
      
    NVIC_InitStrue.NVIC_IRQChannel=USART3_IRQn;  
    NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE;  
    NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=0;  
    NVIC_InitStrue.NVIC_IRQChannelSubPriority=1;  
    NVIC_Init(&NVIC_InitStrue);  
      
}  
  
void USART3_IRQHandler(void)  
{  

     if(USART_GetITStatus(USART3,USART_IT_RXNE)!=RESET)  
 {  
     res= USART_ReceiveData(USART3); 	 
     USART_SendData(USART3,res);    //串口3发送数据给蓝牙模块接收,也就是手机app接收到的数据
  }  
} 

六、PID算法

        PID(Proportional-Integral-Derivative)控制算法是一种用于控制系统的反馈控制算法。它通过比较实际输出与期望目标值之间的差异(误差),来调整控制器的输出,以使系统保持在期望的状态。PID控制算法由三个主要组成部分组成,分别是比例(Proportional)、积分(Integral)和微分(Derivative)。

以下是每个部分的解释以及PID控制的工作原理:

  1. 比例(Proportional)部分:比例部分的作用是根据当前误差的大小来调整控制输出。它与误差成正比,如果误差增大,比例部分的输出也增大,反之亦然。这个部分的作用是快速减小误差,但它单独使用时可能导致系统出现震荡。

  2. 积分(Integral)部分:积分部分的作用是积累误差并进行修正,以减小静态误差。它根据误差的积累来调整控制输出,通常用于减小比例控制导致的静态误差。积分部分的存在可以确保系统最终收敛到期望值,即使存在系统参数变化或外部干扰。

  3. 微分(Derivative)部分:微分部分的作用是预测误差的未来变化趋势,以减小控制输出的突变。它对误差的变化率进行调整,有助于防止系统在接近期望值时出现过冲或震荡。微分部分可以提高系统的稳定性。

PID控制算法的总输出是这三个部分的加权和,通常表示为:

Output=Kp​⋅P+Ki​⋅I+Kd​⋅D

        其中,P 是比例部分的输出,I 是积分部分的输出,D 是微分部分的输出,而 Kp​、Ki​ 和 Kd​ 则是控制器的参数,通过调整这些参数可以实现不同的控制效果。

        PID控制算法在自动化控制系统中广泛应用,如温度控制、电机控制、飞行控制等。通过适当调整参数,PID控制器可以实现系统快速响应、减小静态误差和维持系统稳定性。

PID控制详解

平衡车PID调参

//直立环(比例p、微分d)
int balance(float Angle,float Gyro){
	static float Bias;
	int balance;
	Bias=Angle-ZHONGZHI;
	balance=Bias*Kzp+Gyro*Kzd;
	return balance;
	
}

//速度环(比例p、积分i)
int velocity(int Encoder_left,int Encoder_right){
	static float Velocity, Encoder_Least, Encoder;
	static float Encoder_Integral;

	Ksi=Ksp/200;
	
	Encoder_Least = (Encoder_Left+Encoder_Right)-0;
	Encoder=Encoder*0.7+Encoder_Least*0.3;
	Encoder_Integral=Encoder_Integral+Encoder;                      
	Encoder_Integral=Encoder_Integral-Target_Velocity; //改变Target_Velocity就是控制小车前进后退              
	if(Encoder_Integral>15000)  	Encoder_Integral=15000;        
	if(Encoder_Integral<-15000) 	Encoder_Integral=-15000; 
  	
	Velocity=Encoder*Ksp+Encoder_Integral*Ksi;           
	return Velocity;
	
	
}
//转向环(比例p、微分d)
int Turn(float yaw,float Gyro)
{
	  float Turn;     
	  float Bias;//目标角度	  
      Ktd=Ktp/100;
	  Bias=yaw-0;
	  Turn=Bias*Ktp+Gyro*Ktd;
	  return Turn;
}

平衡小车

平衡小车之家资料

链接: https://pan.baidu.com/s/1J6Rluf0T4mVtif2dqRtWxw?pwd=ghjm 提取码: ghjm 

本人自制平衡车代码

链接: https://pan.baidu.com/s/1J6Rluf0T4mVtif2dqRtWxw?pwd=ghjm 提取码: ghjm 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值